Merge pull request #19 from otiai10/feature/skip

Feature/skip
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 @@
 import (
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	. "github.com/otiai10/mint"
@@ -114,7 +115,7 @@
 	})
 
 	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 @@
 		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 @@
 
 // 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 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 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 @@
 	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