summary history files

internal/cli/copy.go
package cli

import (
	"database/sql"
	"os"
	"path"
	"path/filepath"
	"pt/internal/file"
	"pt/internal/logwrap"
	"pt/internal/worker"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"golang.org/x/sync/errgroup"
)

type result struct {
	f                   file.File
	destinationFilePath string
	err                 error
}

func copyCmd(cli *cli) *cobra.Command {
	var flags struct {
		sourceDir       string
		destinationDir  string
		logLevel        string
		checkDuplicates bool
	}
	var cmd = &cobra.Command{
		Use: "copy",
		PreRun: func(cmd *cobra.Command, args []string) {
			_ = viper.BindPFlag("source-file", cmd.Flags().Lookup("source-file"))
			_ = viper.BindPFlag("source-dir", cmd.Flags().Lookup("source-dir"))
			_ = viper.BindPFlag("destination-dir", cmd.Flags().Lookup("destination-dir"))
			_ = viper.BindPFlag("log-level", cmd.Flags().Lookup("log-level"))
			_ = viper.BindPFlag("check-duplicates", cmd.Flags().Lookup("check-duplicates"))
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			db, err := sql.Open("sqlite3", cli.config.DBFile)
			if err != nil {
				return err
			}
			defer db.Close()

			logger := logwrap.New("pt", os.Stdout, false)
			switch flags.logLevel {
			case "info":
				logger.SetLevel(logwrap.INFO)
			case "debug":
				logger.SetLevel(logwrap.DEBUG)
			default:
				logger.SetLevel(logwrap.NONE)
			}

			if cli.debug {
				logger.SetLevel(logwrap.DEBUG)
			}

			sourceDir := cli.config.SourceDir
			if flags.sourceDir != "" {
				sourceDir = flags.sourceDir
			}

			destinationDir := cli.config.DestinationDir
			if flags.destinationDir != "" {
				destinationDir = flags.destinationDir
			}

			g, ctx := errgroup.WithContext(cmd.Context())
			files := make(chan file.File)

			g.Go(func() error {
				defer close(files)
				return filepath.Walk(sourceDir, func(p string, info os.FileInfo, err error) error {
					if !info.Mode().IsRegular() {
						return nil
					}

					if strings.HasPrefix(path.Base(p), ".") {
						return nil
					}

					if strings.HasPrefix(path.Base(p), ".") {
						return nil
					}

					if strings.ToLower(file.DirName(p)) == ".thumbnails" {
						return nil
					}

					select {
					case files <- file.NewFile(p, info):
					case <-ctx.Done():
						return ctx.Err()
					}
					return nil
				})
			})

			const numCopiers = 4
			for i := 0; i < numCopiers; i++ {
				g.Go(func() error {
					return worker.Copier(ctx, destinationDir, cli.config.DeviceNames, files, flags.checkDuplicates)
				})
			}

			if err := g.Wait(); err != nil {
				return err
			}

			return nil
		},
	}
	cmd.Flags().StringVar(&flags.sourceDir, "source-dir", "", "Source directory")
	cmd.Flags().StringVar(&flags.destinationDir, "destination-dir", "", "Destination directory")
	cmd.Flags().StringVar(&flags.logLevel, "log-level", "none", "Log level (none, info, debug)")
	cmd.Flags().BoolVar(&flags.checkDuplicates, "check-duplicates", false, "Check duplicates within the DB and if found, don't clobber")
	return cmd
}