Skip to content

Commit

Permalink
Merge pull request #101 from michalpristas/feat/onerr
Browse files Browse the repository at this point in the history
Let caller hander error cases
  • Loading branch information
otiai10 committed Apr 19, 2023
2 parents a112cf0 + 2abaf6c commit 4359ba7
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 7 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -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)

Expand Down
40 changes: 40 additions & 0 deletions all_test.go
Expand Up @@ -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
Expand Down
23 changes: 16 additions & 7 deletions copy.go
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
4 changes: 4 additions & 0 deletions options.go
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions 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.

0 comments on commit 4359ba7

Please sign in to comment.