diff --git a/all_test.go b/all_test.go index 8f41734..cd91b80 100644 --- a/all_test.go +++ b/all_test.go @@ -3,6 +3,7 @@ package copy import ( "os" "path/filepath" + "strings" "testing" . "github.com/otiai10/mint" @@ -114,7 +115,7 @@ func TestCopy(t *testing.T) { }) When(t, "try to copy READ-not-allowed source", func(t *testing.T) { - err := Copy("testdata/case06", "testdata.copy/case06") + err := Copy("testdata/doesNotExist", "testdata.copy/doesNotExist") Expect(t, err).Not().ToBe(nil) }) @@ -138,4 +139,21 @@ func TestCopy(t *testing.T) { err = os.Chmod(dest, 0755) Expect(t, err).ToBe(nil) }) + + When(t, "Options.Skip provided", func(t *testing.T) { + opt := Options{Skip: func(src string) bool { return strings.HasSuffix(src, "_skip") }} + err := Copy("testdata/case06", "testdata.copy/case06", opt) + Expect(t, err).ToBe(nil) + info, err := os.Stat("./testdata.copy/case06/dir_skip") + Expect(t, info).ToBe(nil) + Expect(t, os.IsNotExist(err)).ToBe(true) + + info, err = os.Stat("./testdata.copy/case06/file_skip") + Expect(t, info).ToBe(nil) + Expect(t, os.IsNotExist(err)).ToBe(true) + + info, err = os.Stat("./testdata.copy/case06/README.md") + Expect(t, info).Not().ToBe(nil) + Expect(t, err).ToBe(nil) + }) } diff --git a/copy.go b/copy.go index 2fdb3ea..8793276 100644 --- a/copy.go +++ b/copy.go @@ -16,21 +16,26 @@ const ( // Copy copies src to dest, doesn't matter if src is a directory or a file. func Copy(src, dest string, opt ...Options) error { - opt = append(opt, DefaultOptions) info, err := os.Lstat(src) if err != nil { return err } - return copy(src, dest, info, opt[0]) + return copy(src, dest, info, assure(opt...)) } // copy dispatches copy-funcs according to the mode. // Because this "copy" could be called recursively, // "info" MUST be given here, NOT nil. func copy(src, dest string, info os.FileInfo, opt Options) error { + + if opt.Skip(src) { + return nil + } + if info.Mode()&os.ModeSymlink != 0 { return onsymlink(src, dest, info, opt) } + if info.IsDir() { return dcopy(src, dest, info, opt) } @@ -98,10 +103,6 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { func onsymlink(src, dest string, info os.FileInfo, opt Options) error { - if opt.OnSymlink == nil { - opt.OnSymlink = DefaultOptions.OnSymlink - } - switch opt.OnSymlink(src) { case Shallow: return lcopy(src, dest) @@ -133,7 +134,8 @@ func lcopy(src, dest string) error { } // fclose ANYHOW closes file, -// with asiging error occured BUT respecting the error already reported. +// with asiging error raised during Close, +// BUT respecting the error already reported. func fclose(f *os.File, reported *error) { if err := f.Close(); *reported == nil { *reported = err @@ -141,9 +143,25 @@ func fclose(f *os.File, reported *error) { } // chmod ANYHOW changes file mode, -// with asiging error occured BUT respecting the error already reported. +// with asiging error raised during Chmod, +// BUT respecting the error already reported. func chmod(dir string, mode os.FileMode, reported *error) { if err := os.Chmod(dir, mode); *reported == nil { *reported = err } } + +// assure Options struct, should be called only once. +// All optional values MUST NOT BE nil/zero after assured. +func assure(opts ...Options) Options { + if len(opts) == 0 { + return DefaultOptions + } + if opts[0].OnSymlink == nil { + opts[0].OnSymlink = DefaultOptions.OnSymlink + } + if opts[0].Skip == nil { + opts[0].Skip = DefaultOptions.Skip + } + return opts[0] +} diff --git a/options.go b/options.go index 4053a3f..f51cdfb 100644 --- a/options.go +++ b/options.go @@ -4,6 +4,8 @@ package copy type Options struct { // OnSymlink can specify what to do on symlink OnSymlink func(p string) SymlinkAction + // Skip can specify which files should be skipped + Skip func(src string) bool } // SymlinkAction represents what to do on symlink. @@ -23,4 +25,7 @@ var DefaultOptions = Options{ OnSymlink: func(string) SymlinkAction { return Shallow }, + Skip: func(string) bool { + return false + }, } diff --git a/testdata/case06/README.md b/testdata/case06/README.md new file mode 100644 index 0000000..461e401 --- /dev/null +++ b/testdata/case06/README.md @@ -0,0 +1,3 @@ +# Case 06 + +When `Options.Skip` is provided and returns `true`, src files should be skipped. \ No newline at end of file diff --git a/testdata/case06/dir_skip/README.md b/testdata/case06/dir_skip/README.md new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/testdata/case06/dir_skip/README.md @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/testdata/case06/file_skip b/testdata/case06/file_skip new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/testdata/case06/file_skip @@ -0,0 +1 @@ +Hello \ No newline at end of file