From 3e0b2965b47a913e3cb7fa3267bcbd678cb3b41d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 23 Oct 2020 15:21:19 -0600 Subject: [PATCH] tested working (for proper implementations) --- .gitignore | 3 ++ README.md | 7 +++ copy.go | 6 +-- copy_test.go | 81 ++++++++++++++++++++++++++++++-- fixtures/assets.go | 5 ++ fixtures/assets_dev.go | 8 ++++ fixtures/{src => fs}/baz/baz.txt | 0 fixtures/{src => fs}/file-a.txt | 0 fixtures/{src => fs}/foo-sym.txt | 0 fixtures/{src => fs}/foo.txt | 0 fixtures/{src => fs}/hello.txt | 0 fixtures/{src => fs}/world.txt | 0 go.mod | 6 +++ go.sum | 26 ++++++++++ internal/tools/tools.go | 10 ++++ options.go | 6 +-- vendor/modules.txt | 9 ++++ vfs.go | 37 ++++++++------- 18 files changed, 174 insertions(+), 30 deletions(-) create mode 100644 .gitignore create mode 100644 fixtures/assets.go create mode 100644 fixtures/assets_dev.go rename fixtures/{src => fs}/baz/baz.txt (100%) rename fixtures/{src => fs}/file-a.txt (100%) rename fixtures/{src => fs}/foo-sym.txt (100%) rename fixtures/{src => fs}/foo.txt (100%) rename fixtures/{src => fs}/hello.txt (100%) rename fixtures/{src => fs}/world.txt (100%) create mode 100644 go.sum create mode 100644 internal/tools/tools.go create mode 100644 vendor/modules.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a17d54a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*_vfsdata.go +.*.sw* +.DS_Store diff --git a/README.md b/README.md index f81bfa9..6da14ec 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,10 @@ if err := Copy(vfs, ".", "/tmp/dst/"); nil != err { fmt.Fprintf(os.Stderr, "couldn't copy vfs: %v\n", err) } ``` + +## Test + +```bash +go generate ./... +go test ./... +``` diff --git a/copy.go b/copy.go index b35c106..dab677e 100644 --- a/copy.go +++ b/copy.go @@ -2,7 +2,6 @@ package vfscopy import ( "io" - "fmt" "os" "path/filepath" ) @@ -16,7 +15,7 @@ const ( // Copy copies src to dest, doesn't matter if src is a directory or a file. func Copy(vfs FileSystem, src, dest string, opt ...Options) error { - //info, err := fs.Lstat(src) + // FYI: os.Open does a proper lstat f, err := vfs.Open(src) if err != nil { return err @@ -132,10 +131,10 @@ func dcopy(vfs FileSystem, srcdir, destdir string, d File, info os.FileInfo, opt } func onsymlink(vfs FileSystem, src, dest string, opt Options) error { - fmt.Println("lstat happy") switch opt.OnSymlink(src) { case Shallow: return lcopy(vfs, src, dest) + /* case Deep: orig, err := vfs.EvalSymlinks(src) if err != nil { @@ -151,6 +150,7 @@ func onsymlink(vfs FileSystem, src, dest string, opt Options) error { return err } return copy(vfs, orig, dest, f, info, opt) + */ case Skip: fallthrough default: diff --git a/copy_test.go b/copy_test.go index 4fb8e47..ff89909 100644 --- a/copy_test.go +++ b/copy_test.go @@ -1,19 +1,90 @@ package vfscopy import ( + "fmt" + "os" + "path/filepath" + "log" "testing" + "errors" + + "git.rootprojects.org/root/vfscopy/fixtures" ) -func TestRecursiveCopy(t *testing.T) { +func TestNativeRecursiveCopy(t *testing.T) { { opts := Options{ - OnSymlink: func (path string) SymlinkAction { + OnSymlink: func(path string) SymlinkAction { return Shallow }, } - vfs := Dir("./fixtures/src/") - if err := Copy(vfs, ".", "/tmp/dst/", opts,); nil != err { - t.Fatalf("error: %v", err) + vfs := Dir("./fixtures/fs/") + tmpDir := "/tmp/go-vfscopy-dst-1/" + + _ = os.RemoveAll(tmpDir) + defer func () { + _ = os.RemoveAll(tmpDir) + }() + + if err := Copy(vfs, ".", tmpDir, opts); nil != err { + t.Errorf("error: %v", err) + return + } + } +} + +func TestVFSRecursiveCopy(t *testing.T) { + { + opts := Options{ + OnSymlink: func(path string) SymlinkAction { + return Shallow + }, + } + vfs := NewVFS(fixtures.Assets) + tmpDir := "/tmp/go-vfscopy-dst-2/" + + _ = os.RemoveAll(tmpDir) + defer func () { + _ = os.RemoveAll(tmpDir) + }() + + if err := Copy(vfs, ".", tmpDir, opts); nil != err { + t.Errorf("copy error: %v", err) + return + } + root := "fixtures/fs" + walker := func(path string, info os.FileInfo, err error) error { + if nil != err { + return err + } + + rel := path[len(root):] + + s, err := os.Lstat(path) + if nil != err { + return err + } + + dst := filepath.Join(tmpDir, rel) + d, err := os.Lstat(dst) + if nil != err { + return err + } + + if s.Mode() != d.Mode() { + log.Println("implementation does not support permissions and/or symlinks:", s.Mode(), d.Mode()) + //return errors.New("did not copy file mode (e.g. symlink status)") + } else if s.Size() != d.Size() { + return errors.New("did not copy full file size") + } + + fmt.Println(path, "=>", dst) + return nil + } + err := filepath.Walk(root, walker) + if nil != err { + t.Errorf("check dst error: %v", err) + return } } } diff --git a/fixtures/assets.go b/fixtures/assets.go new file mode 100644 index 0000000..f51483f --- /dev/null +++ b/fixtures/assets.go @@ -0,0 +1,5 @@ +// +build !dev + +//go:generate go run -mod vendor github.com/shurcooL/vfsgen/cmd/vfsgendev -source="git.rootprojects.org/root/vfscopy/fixtures".Assets + +package fixtures diff --git a/fixtures/assets_dev.go b/fixtures/assets_dev.go new file mode 100644 index 0000000..d9525ab --- /dev/null +++ b/fixtures/assets_dev.go @@ -0,0 +1,8 @@ +// +build dev + +package fixtures + +import "net/http" + +// Assets is the public file system which should be served by http +var Assets http.FileSystem = http.Dir("./fs") diff --git a/fixtures/src/baz/baz.txt b/fixtures/fs/baz/baz.txt similarity index 100% rename from fixtures/src/baz/baz.txt rename to fixtures/fs/baz/baz.txt diff --git a/fixtures/src/file-a.txt b/fixtures/fs/file-a.txt similarity index 100% rename from fixtures/src/file-a.txt rename to fixtures/fs/file-a.txt diff --git a/fixtures/src/foo-sym.txt b/fixtures/fs/foo-sym.txt similarity index 100% rename from fixtures/src/foo-sym.txt rename to fixtures/fs/foo-sym.txt diff --git a/fixtures/src/foo.txt b/fixtures/fs/foo.txt similarity index 100% rename from fixtures/src/foo.txt rename to fixtures/fs/foo.txt diff --git a/fixtures/src/hello.txt b/fixtures/fs/hello.txt similarity index 100% rename from fixtures/src/hello.txt rename to fixtures/fs/hello.txt diff --git a/fixtures/src/world.txt b/fixtures/fs/world.txt similarity index 100% rename from fixtures/src/world.txt rename to fixtures/fs/world.txt diff --git a/go.mod b/go.mod index 880e9a2..e0fe6d5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,9 @@ module git.rootprojects.org/root/vfscopy go 1.15 + +require ( + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect + github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 + golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d1e5ea0 --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6 h1:rbvTkL9AkFts1cgI78+gG6Yu1pwaqX6hjSJAatB78E4= +golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/tools/tools.go b/internal/tools/tools.go new file mode 100644 index 0000000..1b28f7c --- /dev/null +++ b/internal/tools/tools.go @@ -0,0 +1,10 @@ +// +build tools + +// Package tools is a faux package for tracking dependencies that don't make it into the code +package tools + +import ( + // these are 'go generate' tooling dependencies, not including in the binary + _ "github.com/shurcooL/vfsgen" + _ "github.com/shurcooL/vfsgen/cmd/vfsgendev" +) diff --git a/options.go b/options.go index c529339..c293907 100644 --- a/options.go +++ b/options.go @@ -22,12 +22,10 @@ type Options struct { type SymlinkAction int const ( - // Deep creates hard-copy of contents. - Deep SymlinkAction = iota // Shallow creates new symlink to the dest of symlink. - Shallow + Shallow SymlinkAction = iota // Skip does nothing with symlink. - Skip + Skip ) // getDefaultOptions provides default options, diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..c8a3867 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,9 @@ +# github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 +## explicit +github.com/shurcooL/httpfs/vfsutil +# github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 +## explicit +github.com/shurcooL/vfsgen +github.com/shurcooL/vfsgen/cmd/vfsgendev +# golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6 +## explicit diff --git a/vfs.go b/vfs.go index 51c0112..2cc88db 100644 --- a/vfs.go +++ b/vfs.go @@ -3,22 +3,19 @@ package vfscopy import ( "os" "io" + "io/ioutil" "path/filepath" "path" "strings" "errors" + "net/http" ) -// HTTPFileSystem is copied from http.FileSystem -type HTTPFileSystem interface { - Open(name string) (File, error) -} - // FileSystem is a Virtual FileSystem with Symlink support type FileSystem interface { Open(name string) (File, error) Readlink(name string) (string, error) - EvalSymlinks(name string) (string, error) + //EvalSymlinks(name string) (string, error) } // File is copied from http.File @@ -30,30 +27,32 @@ type File interface { Stat() (os.FileInfo, error) } -// VFS is a virtual FileSystem with possible Symlink support +// VFS is a virtual FileSystem with Symlink support type VFS struct { - FileSystem HTTPFileSystem + FileSystem http.FileSystem } // Open opens a file relative to a virtual filesystem func (v *VFS) Open(name string) (File, error) { - return v.Open(name) + return v.FileSystem.Open(name) } // Readlink returns a "not implemented" error, // which is okay because it is never called for http.FileSystem. func (v *VFS) Readlink(name string) (string, error) { - return "", errors.New("not implemented") + f, err := v.FileSystem.Open(name) + if nil != err { + return "", err + } + b, err := ioutil.ReadAll(f) + if nil != err { + return "", err + } + return string(b), nil } -// EvalSymlinks returns the original name, -// which is okay because it is never called for http.FileSystem. -func (v *VFS) EvalSymlinks(name string) (string, error) { - return name, nil -} - -// NewVFS gives an http.FileSystem faux symlink support -func NewVFS(httpfs HTTPFileSystem) FileSystem { +// NewVFS gives an http.FileSystem (real) symlink support +func NewVFS(httpfs http.FileSystem) FileSystem { return &VFS{ FileSystem: httpfs } } @@ -122,6 +121,7 @@ func (d Dir) Readlink(name string) (string, error) { return name, nil } +/* // EvalSymlinks returns the destination of the named symbolic link. func (d Dir) EvalSymlinks(name string) (string, error) { name, err := d.fullName(name) @@ -134,3 +134,4 @@ func (d Dir) EvalSymlinks(name string) (string, error) { } return name, nil } +*/