Skip to content

Commit

Permalink
Merge pull request #108 from otiai10/develop
Browse files Browse the repository at this point in the history
Support embed.FS (fs.FS)
  • Loading branch information
otiai10 authored Jun 18, 2023
2 parents 4359ba7 + 5c1a705 commit f0f65b5
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 12 deletions.
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

0 comments on commit f0f65b5

Please sign in to comment.