diff --git a/cmd/check/check.go b/cmd/check/check.go index 3ab5c06ab..d7675d4a8 100644 --- a/cmd/check/check.go +++ b/cmd/check/check.go @@ -4,8 +4,6 @@ package check import ( "context" "fmt" - "io" - "os" "strings" "github.com/rclone/rclone/cmd" @@ -78,58 +76,23 @@ option for more information.`, "|", "`") // GetCheckOpt gets the options corresponding to the check flags func GetCheckOpt(fsrc, fdst fs.Fs) (opt *operations.CheckOpt, close func(), err error) { - closers := []io.Closer{} - opt = &operations.CheckOpt{ Fsrc: fsrc, Fdst: fdst, OneWay: oneway, } - open := func(name string, pout *io.Writer) error { - if name == "" { - return nil - } - if name == "-" { - *pout = os.Stdout - return nil - } - out, err := os.Create(name) - if err != nil { - return err - } - *pout = out - closers = append(closers, out) - return nil - } - - if err = open(combined, &opt.Combined); err != nil { + close, err = operations.OpenReportFiles( + operations.ReportFile{Name: combined, Out: &opt.Combined}, + operations.ReportFile{Name: missingOnSrc, Out: &opt.MissingOnSrc}, + operations.ReportFile{Name: missingOnDst, Out: &opt.MissingOnDst}, + operations.ReportFile{Name: match, Out: &opt.Match}, + operations.ReportFile{Name: differ, Out: &opt.Differ}, + operations.ReportFile{Name: errFile, Out: &opt.Error}, + ) + if err != nil { return nil, nil, err } - if err = open(missingOnSrc, &opt.MissingOnSrc); err != nil { - return nil, nil, err - } - if err = open(missingOnDst, &opt.MissingOnDst); err != nil { - return nil, nil, err - } - if err = open(match, &opt.Match); err != nil { - return nil, nil, err - } - if err = open(differ, &opt.Differ); err != nil { - return nil, nil, err - } - if err = open(errFile, &opt.Error); err != nil { - return nil, nil, err - } - - close = func() { - for _, closer := range closers { - err := closer.Close() - if err != nil { - fs.Errorf(nil, "Failed to close report output: %v", err) - } - } - } return opt, close, nil } diff --git a/fs/operations/logger.go b/fs/operations/logger.go index f9fb41f0e..4f4a501e0 100644 --- a/fs/operations/logger.go +++ b/fs/operations/logger.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "os" mutex "sync" "github.com/rclone/rclone/fs" @@ -302,6 +303,48 @@ func WinningSide(ctx context.Context, sigil Sigil, src, dst fs.DirEntry, err err return winner } +// ReportFile pairs the name of a file a report is written to with +// the writer to set when it is opened. +type ReportFile struct { + Name string // file name, "" for no report or "-" for stdout + Out *io.Writer // set to the opened file +} + +// OpenReportFiles opens the report files for writing and returns a +// function to close them. +// +// For each file an empty Name leaves Out unchanged so the report is +// not written, "-" sets it to stdout and any other Name is created as +// a file, truncating it if it exists. +func OpenReportFiles(files ...ReportFile) (close func(), err error) { + closers := []io.Closer{} + close = func() { + for _, closer := range closers { + err := closer.Close() + if err != nil { + fs.Errorf(nil, "Failed to close report output: %v", err) + } + } + } + for _, file := range files { + switch file.Name { + case "": + continue + case "-": + *file.Out = os.Stdout + default: + out, err := os.Create(file.Name) + if err != nil { + close() + return nil, err + } + *file.Out = out + closers = append(closers, out) + } + } + return close, nil +} + // NewSyncLoggerOpt returns a LoggerOpt with no report writers set and // the default listing options for the DestAfter report. func NewSyncLoggerOpt() LoggerOpt { diff --git a/fs/operations/operationsflags/operationsflags.go b/fs/operations/operationsflags/operationsflags.go index e2cfda034..d48acee6d 100644 --- a/fs/operations/operationsflags/operationsflags.go +++ b/fs/operations/operationsflags/operationsflags.go @@ -5,8 +5,6 @@ package operationsflags import ( "context" _ "embed" - "io" - "os" "strings" "github.com/rclone/rclone/fs" @@ -76,55 +74,18 @@ func AddLoggerFlags(cmdFlags *pflag.FlagSet, opt *operations.LoggerOpt, flagsOpt // ConfigureLoggers verifies and sets up writers for log files requested via CLI flags func ConfigureLoggers(ctx context.Context, fdst fs.Fs, command *cobra.Command, opt *operations.LoggerOpt, flagsOpt AddLoggerFlagsOptions) (func(), error) { - closers := []io.Closer{} - - open := func(name string, pout *io.Writer) error { - if name == "" { - return nil - } - if name == "-" { - *pout = os.Stdout - return nil - } - out, err := os.Create(name) - if err != nil { - return err - } - *pout = out - closers = append(closers, out) - return nil - } - - if err := open(flagsOpt.Combined, &opt.Combined); err != nil { + close, err := operations.OpenReportFiles( + operations.ReportFile{Name: flagsOpt.Combined, Out: &opt.Combined}, + operations.ReportFile{Name: flagsOpt.MissingOnSrc, Out: &opt.MissingOnSrc}, + operations.ReportFile{Name: flagsOpt.MissingOnDst, Out: &opt.MissingOnDst}, + operations.ReportFile{Name: flagsOpt.Match, Out: &opt.Match}, + operations.ReportFile{Name: flagsOpt.Differ, Out: &opt.Differ}, + operations.ReportFile{Name: flagsOpt.ErrFile, Out: &opt.Error}, + operations.ReportFile{Name: flagsOpt.DestAfter, Out: &opt.DestAfter}, + ) + if err != nil { return nil, err } - if err := open(flagsOpt.MissingOnSrc, &opt.MissingOnSrc); err != nil { - return nil, err - } - if err := open(flagsOpt.MissingOnDst, &opt.MissingOnDst); err != nil { - return nil, err - } - if err := open(flagsOpt.Match, &opt.Match); err != nil { - return nil, err - } - if err := open(flagsOpt.Differ, &opt.Differ); err != nil { - return nil, err - } - if err := open(flagsOpt.ErrFile, &opt.Error); err != nil { - return nil, err - } - if err := open(flagsOpt.DestAfter, &opt.DestAfter); err != nil { - return nil, err - } - - close := func() { - for _, closer := range closers { - err := closer.Close() - if err != nil { - fs.Errorf(nil, "Failed to close report output: %v", err) - } - } - } opt.Init(ctx, fdst, command.Flags())