diff --git a/commands/cli/parse.go b/commands/cli/parse.go index ece7014fbfdacb5ebc3936895761c084ae7bd65e..49817475e1c25018165ff094e7e80de3bb99820c 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -64,7 +64,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c } req.SetArguments(stringArgs) - file := &files.SliceFile{"", fileArgs} + file := files.NewSliceFile("", fileArgs) req.SetFiles(file) err = cmd.CheckArguments(req) @@ -298,7 +298,7 @@ func appendFile(args []files.File, inputs []string, argDef *cmds.Argument, recur } func appendStdinAsFile(args []files.File, stdin *os.File) ([]files.File, *os.File) { - arg := &files.ReaderFile{"", stdin} + arg := files.NewReaderFile("", stdin, nil) return append(args, arg), nil } diff --git a/commands/files/file.go b/commands/files/file.go index 9e9b043a1854f5df7df5d33be5ffb21672eab003..8f76ea71d6360553256f52c0434c15894e8102ef 100644 --- a/commands/files/file.go +++ b/commands/files/file.go @@ -3,6 +3,7 @@ package files import ( "errors" "io" + "os" ) var ( @@ -29,3 +30,16 @@ type File interface { // If the file is a regular file (not a directory), NextFile will return a non-nil error. NextFile() (File, error) } + +type StatFile interface { + File + + Stat() os.FileInfo +} + +type PeekFile interface { + File + + Peek(n int) File + Length() int +} diff --git a/commands/files/file_test.go b/commands/files/file_test.go index a9499fb78860d9dd7e4f7fd3ced4d7d2ef99e98d..01b7a9d02fd3d1c80c3c91c1c154f4a2445e5faa 100644 --- a/commands/files/file_test.go +++ b/commands/files/file_test.go @@ -11,13 +11,13 @@ import ( func TestSliceFiles(t *testing.T) { name := "testname" files := []File{ - &ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n"))}, - &ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))}, - &ReaderFile{"boop.txt", ioutil.NopCloser(strings.NewReader("boop"))}, + NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n")), nil), + NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil), + NewReaderFile("boop.txt", ioutil.NopCloser(strings.NewReader("boop")), nil), } buf := make([]byte, 20) - sf := &SliceFile{name, files} + sf := NewSliceFile(name, files) if !sf.IsDirectory() { t.Error("SliceFile should always be a directory") @@ -55,7 +55,7 @@ func TestSliceFiles(t *testing.T) { func TestReaderFiles(t *testing.T) { message := "beep boop" - rf := &ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader(message))} + rf := NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader(message)), nil) buf := make([]byte, len(message)) if rf.IsDirectory() { diff --git a/commands/files/readerfile.go b/commands/files/readerfile.go index af88562fd17f462f39ae832e5d934d3c7fb25b48..38c28efe3a5d39920c52a8bf3013d3ee8e00ef51 100644 --- a/commands/files/readerfile.go +++ b/commands/files/readerfile.go @@ -1,12 +1,20 @@ package files -import "io" +import ( + "io" + "os" +) // ReaderFile is a implementation of File created from an `io.Reader`. // ReaderFiles are never directories, and can be read from and closed. type ReaderFile struct { - Filename string - Reader io.ReadCloser + filename string + reader io.ReadCloser + stat os.FileInfo +} + +func NewReaderFile(filename string, reader io.ReadCloser, stat os.FileInfo) *ReaderFile { + return &ReaderFile{filename, reader, stat} } func (f *ReaderFile) IsDirectory() bool { @@ -18,13 +26,17 @@ func (f *ReaderFile) NextFile() (File, error) { } func (f *ReaderFile) FileName() string { - return f.Filename + return f.filename } func (f *ReaderFile) Read(p []byte) (int, error) { - return f.Reader.Read(p) + return f.reader.Read(p) } func (f *ReaderFile) Close() error { - return f.Reader.Close() + return f.reader.Close() +} + +func (f *ReaderFile) Stat() os.FileInfo { + return f.stat } diff --git a/commands/files/serialfile.go b/commands/files/serialfile.go index 21f3a9bb9cd757d106ff841e84ee133ff70298e5..9a81f8bd17bb433e7662e5e6f9162682cf7876b4 100644 --- a/commands/files/serialfile.go +++ b/commands/files/serialfile.go @@ -20,6 +20,7 @@ func (es sortFIByName) Less(i, j int) bool { return es[i].Name() < es[j].Name() type serialFile struct { path string files []os.FileInfo + stat os.FileInfo current *os.File } @@ -35,7 +36,7 @@ func NewSerialFile(path string, file *os.File) (File, error) { func newSerialFile(path string, file *os.File, stat os.FileInfo) (File, error) { // for non-directories, return a ReaderFile if !stat.IsDir() { - return &ReaderFile{path, file}, nil + return &ReaderFile{path, file, stat}, nil } // for directories, stat all of the contents first, so we know what files to @@ -55,7 +56,7 @@ func newSerialFile(path string, file *os.File, stat os.FileInfo) (File, error) { // make sure contents are sorted so -- repeatably -- we get the same inputs. sort.Sort(sortFIByName(contents)) - return &serialFile{path, contents, nil}, nil + return &serialFile{path, contents, stat, nil}, nil } func (f *serialFile) IsDirectory() bool { @@ -113,3 +114,7 @@ func (f *serialFile) Close() error { return nil } + +func (f *serialFile) Stat() os.FileInfo { + return f.stat +} diff --git a/commands/files/slicefile.go b/commands/files/slicefile.go index e1035f2ce3611bf0221b2e9a4374806c068edfd2..fe0332d590eb9f175c7f8f58c745573d83736476 100644 --- a/commands/files/slicefile.go +++ b/commands/files/slicefile.go @@ -6,8 +6,13 @@ import "io" // It contains children files, and is created from a `[]File`. // SliceFiles are always directories, and can't be read from or closed. type SliceFile struct { - Filename string - Files []File + filename string + files []File + n int +} + +func NewSliceFile(filename string, files []File) *SliceFile { + return &SliceFile{filename, files, 0} } func (f *SliceFile) IsDirectory() bool { @@ -15,16 +20,16 @@ func (f *SliceFile) IsDirectory() bool { } func (f *SliceFile) NextFile() (File, error) { - if len(f.Files) == 0 { + if f.n >= len(f.files) { return nil, io.EOF } - file := f.Files[0] - f.Files = f.Files[1:] + file := f.files[f.n] + f.n++ return file, nil } func (f *SliceFile) FileName() string { - return f.Filename + return f.filename } func (f *SliceFile) Read(p []byte) (int, error) { @@ -34,3 +39,11 @@ func (f *SliceFile) Read(p []byte) (int, error) { func (f *SliceFile) Close() error { return ErrNotReader } + +func (f *SliceFile) Peek(n int) File { + return f.files[n] +} + +func (f *SliceFile) Length() int { + return len(f.files) +} diff --git a/commands/http/multifilereader_test.go b/commands/http/multifilereader_test.go index 8d832c9ccc97bcbbb59ab418efac0d70c01395c7..8242e277779e87f6dbd43f6b101c332cb2513645 100644 --- a/commands/http/multifilereader_test.go +++ b/commands/http/multifilereader_test.go @@ -13,14 +13,14 @@ import ( func TestOutput(t *testing.T) { text := "Some text! :)" fileset := []files.File{ - &files.ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader(text))}, - &files.SliceFile{"boop", []files.File{ - &files.ReaderFile{"boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep"))}, - &files.ReaderFile{"boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop"))}, - }}, - &files.ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))}, - } - sf := &files.SliceFile{"", fileset} + files.NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader(text)), nil), + files.NewSliceFile("boop", []files.File{ + files.NewReaderFile("boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep")), nil), + files.NewReaderFile("boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop")), nil), + }), + files.NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil), + } + sf := files.NewSliceFile("", fileset) buf := make([]byte, 20) // testing output by reading it with the go stdlib "mime/multipart" Reader