diff --git a/README.md b/README.md index 32edfd8..dc66c4e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ type Options struct { // OnDirExists can specify what to do when there is a directory already existing in destination. OnDirExists func(src, dest string) DirExistsAction + // OnError can let users decide how to handle errors (e.g., you can suppress specific error). + OnError func(src, dest, string, err error) error + // Skip can specify which files should be skipped Skip func(srcinfo os.FileInfo, src, dest string) (bool, error) diff --git a/all_test.go b/all_test.go index 3c43b2d..3a24744 100644 --- a/all_test.go +++ b/all_test.go @@ -405,6 +405,46 @@ func TestOptions_CopyRateLimit(t *testing.T) { Expect(t, elapsed > 5*time.Second).ToBe(true) } +func TestOptions_OnFileError(t *testing.T) { + opt := Options{ + OnError: nil, + } + + // existing, process nromally + err := Copy("test/data/case17", "test/data.copy/case17", opt) + Expect(t, err).ToBe(nil) + + // not existing, process err + err = Copy("test/data/case17/non-existing", "test/data.copy/case17/non-existing", opt) + Expect(t, os.IsNotExist(err)).ToBe(true) + + _, err = os.Stat("test/data.copy/case17/non-existing") + Expect(t, os.IsNotExist(err)).ToBe(true) + + // existing, nil err not passed + opt.OnError = func(_, _ string, err error) error { + return err + } + err = Copy("test/data/case17", "test/data.copy/case17", opt) + Expect(t, err).ToBe(nil) + + // not existing, process err + opt.OnError = func(_, _ string, err error) error { return err } + err = Copy("test/data/case17/non-existing", "test/data.copy/case17/non-existing", opt) + Expect(t, os.IsNotExist(err)).ToBe(true) + + _, err = os.Stat("test/data.copy/case17/non-existing") + Expect(t, os.IsNotExist(err)).ToBe(true) + + // not existing, ignore err + opt.OnError = func(_, _ string, err error) error { return nil } + err = Copy("test/data/case17/non-existing", "test/data.copy/case17/non-existing", opt) + Expect(t, err).ToBe(nil) + + _, err = os.Stat("test/data.copy/case17/non-existing") + Expect(t, os.IsNotExist(err)).ToBe(true) +} + type SleepyReader struct { src *os.File sec time.Duration diff --git a/copy.go b/copy.go index d0450e7..a84ff7b 100644 --- a/copy.go +++ b/copy.go @@ -15,20 +15,20 @@ type timespec struct { } // Copy copies src to dest, doesn't matter if src is a directory or a file. -func Copy(src, dest string, opt ...Options) error { +func Copy(src, dest string, opts ...Options) error { + opt := assureOptions(src, dest, opts...) info, err := os.Lstat(src) if err != nil { - return err + return onError(src, dest, err, opt) } - return switchboard(src, dest, info, assureOptions(src, dest, opt...)) + return switchboard(src, dest, info, opt) } // switchboard switches proper copy functions regarding file type, etc... // If there would be anything else here, add a case to this switchboard. func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) { - if info.Mode()&os.ModeDevice != 0 && !opt.Specials { - return err + return onError(src, dest, err, opt) } switch { @@ -42,7 +42,7 @@ func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) { err = fcopy(src, dest, info, opt) } - return err + return onError(src, dest, err, opt) } // copyNextOrSkip decide if this src should be copied or not. @@ -132,7 +132,6 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { // with scanning contents inside the directory // and pass everything to "copy" recursively. func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { - if skip, err := onDirExists(opt, srcdir, destdir); err != nil { return err } else if skip { @@ -243,3 +242,13 @@ func fclose(f *os.File, reported *error) { *reported = err } } + +// onError lets caller to handle errors +// occured when copying a file. +func onError(src, dest string, err error, opt Options) error { + if opt.OnError == nil { + return err + } + + return opt.OnError(src, dest, err) +} diff --git a/options.go b/options.go index bd18d56..52d636c 100644 --- a/options.go +++ b/options.go @@ -14,6 +14,9 @@ type Options struct { // OnDirExists can specify what to do when there is a directory already existing in destination. OnDirExists func(src, dest string) DirExistsAction + // OnErr lets called decide whether or not to continue on particular copy error. + OnError func(src, dest string, err error) error + // Skip can specify which files should be skipped Skip func(srcinfo os.FileInfo, src, dest string) (bool, error) @@ -95,6 +98,7 @@ func getDefaultOptions(src, dest string) Options { return Shallow // Do shallow copy }, OnDirExists: nil, // Default behavior is "Merge". + OnError: nil, // Default is "accept error" Skip: nil, // Do not skip anything AddPermission: 0, // Add nothing PermissionControl: PerservePermission, // Just preserve permission diff --git a/test/data/case17/README.md b/test/data/case17/README.md new file mode 100644 index 0000000..bfce975 --- /dev/null +++ b/test/data/case17/README.md @@ -0,0 +1,5 @@ +So if you wanted to ignore error you should add something like this: +```go +opt.OnError = func(src, dst string, _ error) error { return nil } +``` +The default value is nil and accepts raised error. \ No newline at end of file