Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support embed.FS (fs.FS) #108

Merged
merged 4 commits into from
Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.15', '1.16', '1.17', '1.18', '1.19', '1.20']
go: ['1.18', '1.19', '1.20']
steps:

- name: Set up Go
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/vagrant-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
vagrant-test:
runs-on: macos-10.15
runs-on: macos-12
strategy:
matrix:
runtime:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
go: ['1.17', '1.18', '1.19', '1.20']
go: ['1.18', '1.19', '1.20']
steps:

- name: Set up Go
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ type Options struct {
// If zero, the internal default buffer of 32KB is used.
// See https://golang.org/pkg/io/#CopyBuffer for more information.
CopyBufferSize uint

// If you want to add some limitation on reading src file,
// you can wrap the src and provide new reader,
// such as `RateLimitReader` in the test case.
WrapReader func(src io.Reader) io.Reader

// If given, copy.Copy refers to this fs.FS instead of the OS filesystem.
// e.g., You can use embed.FS to copy files from embedded filesystem.
FS fs.FS
}
```

Expand Down
19 changes: 17 additions & 2 deletions all_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package copy

import (
"embed"
"errors"
"io"
"io/ioutil"
Expand All @@ -14,6 +15,9 @@ import (
. "github.com/otiai10/mint"
)

//go:embed test/data/case18/assets
var assets embed.FS

func TestMain(m *testing.M) {
setup(m)
code := m.Run()
Expand All @@ -26,6 +30,8 @@ func teardown(m *testing.M) {
os.RemoveAll("test/data.copy")
os.RemoveAll("test/data.copyTime")
os.RemoveAll("test/owned-by-root") // Do not check the error ;)
Copy("test/data/case18/assets.backup", "test/data/case18/assets")
os.RemoveAll("test/data/case18/assets.backup")
}

func TestCopy(t *testing.T) {
Expand Down Expand Up @@ -394,7 +400,7 @@ func TestOptions_CopyRateLimit(t *testing.T) {
return
}

opt := Options{WrapReader: func(src *os.File) io.Reader {
opt := Options{WrapReader: func(src io.Reader) io.Reader {
return &SleepyReader{src, 1}
}}

Expand Down Expand Up @@ -445,8 +451,17 @@ func TestOptions_OnFileError(t *testing.T) {
Expect(t, os.IsNotExist(err)).ToBe(true)
}

func TestOptions_FS(t *testing.T) {
os.RemoveAll("test/data/case18/assets")
err := Copy("test/data/case18/assets", "test/data.copy/case18/assets", Options{
FS: assets,
PermissionControl: AddPermission(200), // FIXME
})
Expect(t, err).ToBe(nil)
}

type SleepyReader struct {
src *os.File
src io.Reader
sec time.Duration
}

Expand Down
42 changes: 36 additions & 6 deletions copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package copy

import (
"io"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -17,6 +18,13 @@ type timespec struct {
// Copy copies src to dest, doesn't matter if src is a directory or a file.
func Copy(src, dest string, opts ...Options) error {
opt := assureOptions(src, dest, opts...)
if opt.FS != nil {
info, err := fs.Stat(opt.FS, src)
if err != nil {
return onError(src, dest, err, opt)
}
return switchboard(src, dest, info, opt)
}
info, err := os.Lstat(src)
if err != nil {
return onError(src, dest, err, opt)
Expand Down Expand Up @@ -65,14 +73,20 @@ func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
// with considering existence of parent directory
// and file permission.
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
s, err := os.Open(src)

var readcloser io.ReadCloser
if opt.FS != nil {
readcloser, err = opt.FS.Open(src)
} else {
readcloser, err = os.Open(src)
}
if err != nil {
if os.IsNotExist(err) {
return nil
}
return
}
defer fclose(s, &err)
defer fclose(readcloser, &err)

if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return
Expand All @@ -92,10 +106,10 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {

var buf []byte = nil
var w io.Writer = f
var r io.Reader = s
var r io.Reader = readcloser

if opt.WrapReader != nil {
r = opt.WrapReader(s)
r = opt.WrapReader(r)
}

if opt.CopyBufferSize != 0 {
Expand Down Expand Up @@ -145,7 +159,23 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
}
defer chmodfunc(&err)

contents, err := ioutil.ReadDir(srcdir)
var contents []os.FileInfo
if opt.FS != nil {
entries, err := fs.ReadDir(opt.FS, srcdir)
if err != nil {
return err
}
for _, e := range entries {
info, err := e.Info()
if err != nil {
return err
}
contents = append(contents, info)
}
} else {
contents, err = ioutil.ReadDir(srcdir)
}

if err != nil {
if os.IsNotExist(err) {
return nil
Expand Down Expand Up @@ -237,7 +267,7 @@ func lcopy(src, dest string) error {
// fclose ANYHOW closes file,
// with asiging error raised during Close,
// BUT respecting the error already reported.
func fclose(f *os.File, reported *error) {
func fclose(f io.Closer, reported *error) {
if err := f.Close(); *reported == nil {
*reported = err
}
Expand Down
7 changes: 6 additions & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package copy

import (
"io"
"io/fs"
"os"
)

Expand Down Expand Up @@ -58,7 +59,11 @@ type Options struct {
// If you want to add some limitation on reading src file,
// you can wrap the src and provide new reader,
// such as `RateLimitReader` in the test case.
WrapReader func(src *os.File) io.Reader
WrapReader func(src io.Reader) io.Reader

// If given, copy.Copy refers to this fs.FS instead of the OS filesystem.
// e.g., You can use embed.FS to copy files from embedded filesystem.
FS fs.FS

intent struct {
src string
Expand Down
1 change: 1 addition & 0 deletions test/data/case18/assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Hello
1 change: 1 addition & 0 deletions test_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ func setup(m *testing.M) {
os.Chmod("test/data/case07/dir_0555", 0o555)
os.Chmod("test/data/case07/file_0444", 0o444)
syscall.Mkfifo("test/data/case11/foo/bar", 0o555)
Copy("test/data/case18/assets", "test/data/case18/assets.backup")
}
1 change: 1 addition & 0 deletions test_setup_x.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
)

func setup(m *testing.M) {
os.RemoveAll("test/data.copy")
os.MkdirAll("test/data.copy", os.ModePerm)
os.Symlink("test/data/case01", "test/data/case03/case01")
os.Chmod("test/data/case07/dir_0555", 0555)
Expand Down