From cfdeccb3723af80fe98b9b3103eff1501527b6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 24 Oct 2018 16:48:13 +0200 Subject: [PATCH] Allow Close on dirs, make sure errors make sense --- file.go | 19 ++++++++++++++----- file_test.go | 10 +++++----- multifilereader.go | 8 ++++++-- multipartfile.go | 7 ++----- readerfile.go | 3 +-- serialfile.go | 2 +- slicefile.go | 4 ++-- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/file.go b/file.go index ceb60dd..daa30b8 100644 --- a/file.go +++ b/file.go @@ -15,11 +15,10 @@ var ( // File is an interface that provides functionality for handling // files/directories as values that can be supplied to commands. For -// directories, child files are accessed serially by calling `Files()` -// or `Walk()`. +// directories, child files are accessed serially by calling `NextFile()` // -// Read/Seek/Close methods are only valid for files -// Files/Walk methods are only valid for directories +// Read/Seek methods are only valid for files +// NextFile method is only valid for directories type File interface { io.Reader io.Closer @@ -37,6 +36,16 @@ type File interface { // directory). It will return io.EOF if no more files are // available. If the file is a regular file (not a directory), NextFile // will return a non-nil error. + // + // Note: + // - Some implementations may only allow reading in order - if a + // child directory is returned, you need to read all it's children + // first before calling NextFile on parent again. Before doing parallel + // reading or reading entire level at once, make sure the implementation + // you are using allows that + // - Returned files may not be sorted + // - Depending on implementation it may not be safe to iterate multiple + // children in parallel NextFile() (string, File, error) } @@ -44,7 +53,7 @@ type File interface { type FileInfo interface { File - // AbsPath returns full/real file path. + // AbsPath returns full real file path. AbsPath() string // Stat returns os.Stat of this file diff --git a/file_test.go b/file_test.go index 60d1bc4..e68540c 100644 --- a/file_test.go +++ b/file_test.go @@ -22,12 +22,12 @@ func TestSliceFiles(t *testing.T) { t.Fatal("SliceFile should always be a directory") } - if n, err := sf.Read(buf); n > 0 || err != io.EOF { + if n, err := sf.Read(buf); n > 0 || err != ErrNotReader { t.Fatal("Shouldn't be able to read data from a SliceFile") } - if err := sf.Close(); err != ErrNotReader { - t.Fatal("Shouldn't be able to call `Close` on a SliceFile") + if err := sf.Close(); err != nil { + t.Fatal("Should be able to call `Close` on a SliceFile") } _, file, err := sf.NextFile() @@ -151,8 +151,8 @@ anotherfile if n, err := mpf.Read(buf); n > 0 || err != ErrNotReader { t.Fatal("Shouldn't be able to call `Read` on a directory") } - if err := mpf.Close(); err != ErrNotReader { - t.Fatal("Shouldn't be able to call `Close` on a directory") + if err := mpf.Close(); err != nil { + t.Fatal("Should be able to call `Close` on a directory") } // test properties of file created from third part (nested file) diff --git a/multifilereader.go b/multifilereader.go index 7863c9d..85e66e7 100644 --- a/multifilereader.go +++ b/multifilereader.go @@ -20,7 +20,7 @@ type MultiFileReader struct { files []File path []string - currentFile io.Reader + currentFile File buf bytes.Buffer mpWriter *multipart.Writer closed bool @@ -121,7 +121,11 @@ func (mfr *MultiFileReader) Read(buf []byte) (written int, err error) { // otherwise, read from file data written, err = mfr.currentFile.Read(buf) - if err == io.EOF { + if err == io.EOF || err == ErrNotReader { + if err := mfr.currentFile.Close(); err != nil { + return written, err + } + mfr.currentFile = nil return written, nil } diff --git a/multipartfile.go b/multipartfile.go index a304163..cc94f17 100644 --- a/multipartfile.go +++ b/multipartfile.go @@ -136,9 +136,6 @@ func (f *MultipartFile) Read(p []byte) (int, error) { } func (f *MultipartFile) Close() error { - if f.IsDirectory() { - return ErrNotReader - } return f.Part.Close() } @@ -146,11 +143,11 @@ func (f *MultipartFile) Seek(offset int64, whence int) (int64, error) { if f.IsDirectory() { return 0, ErrNotReader } - return 0, ErrNotReader + return 0, ErrNotSupported } func (f *MultipartFile) Size() (int64, error) { - return 0, ErrNotReader + return 0, ErrNotSupported } type PartReader interface { diff --git a/readerfile.go b/readerfile.go index e40032c..070ee97 100644 --- a/readerfile.go +++ b/readerfile.go @@ -1,7 +1,6 @@ package files import ( - "errors" "io" "os" "path/filepath" @@ -54,7 +53,7 @@ func (f *ReaderFile) Stat() os.FileInfo { func (f *ReaderFile) Size() (int64, error) { if f.stat == nil { - return 0, errors.New("file size unknown") + return 0, ErrNotSupported } return f.stat.Size(), nil } diff --git a/serialfile.go b/serialfile.go index eb41976..71855d5 100644 --- a/serialfile.go +++ b/serialfile.go @@ -98,7 +98,7 @@ func (f *serialFile) NextFile() (string, File, error) { } func (f *serialFile) Read(p []byte) (int, error) { - return 0, io.EOF + return 0, ErrNotReader } func (f *serialFile) Close() error { diff --git a/slicefile.go b/slicefile.go index d356465..5fea5a6 100644 --- a/slicefile.go +++ b/slicefile.go @@ -35,11 +35,11 @@ func (f *SliceFile) NextFile() (string, File, error) { } func (f *SliceFile) Read(p []byte) (int, error) { - return 0, io.EOF + return 0, ErrNotReader } func (f *SliceFile) Close() error { - return ErrNotReader + return nil } func (f *SliceFile) Seek(offset int64, whence int) (int64, error) {