commands/files: Added PeekFile and StatFile interfaces
squash! commands/files: Added PeekFile and StatFile interfaces commands/http: Updated test
Matt Bell committed
Jan 21, 2015 at 17:36 UTC
8ca8d6ef71909d80aeb9771ca3790512cfe21482
7 files changed
+73
-29
commands/cli/parse.go
+2
-2
@@ -64,7 +64,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
64
}
65
req.SetArguments(stringArgs)
66
67
- file := &files.SliceFile{"", fileArgs}
67
+ file := files.NewSliceFile("", fileArgs)
68
req.SetFiles(file)
69
70
err = cmd.CheckArguments(req)
@@ -298,7 +298,7 @@ func appendFile(args []files.File, inputs []string, argDef *cmds.Argument, recur
298
}
299
300
func appendStdinAsFile(args []files.File, stdin *os.File) ([]files.File, *os.File) {
301
- arg := &files.ReaderFile{"", stdin}
301
+ arg := files.NewReaderFile("", stdin, nil)
302
return append(args, arg), nil
303
}
304
commands/files/file.go
+14
@@ -3,6 +3,7 @@ package files
3
import (
4
"errors"
5
"io"
6
+ "os"
7
)
8
9
var (
@@ -29,3 +30,16 @@ type File interface {
30
// If the file is a regular file (not a directory), NextFile will return a non-nil error.
31
NextFile() (File, error)
32
}
33
+
34
+type StatFile interface {
35
+ File
36
+
37
+ Stat() os.FileInfo
38
+}
39
+
40
+type PeekFile interface {
41
+ File
42
+
43
+ Peek(n int) File
44
+ Length() int
45
+}
commands/files/file_test.go
+5
-5
@@ -11,13 +11,13 @@ import (
11
func TestSliceFiles(t *testing.T) {
12
name := "testname"
13
files := []File{
14
- &ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n"))},
15
- &ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))},
16
- &ReaderFile{"boop.txt", ioutil.NopCloser(strings.NewReader("boop"))},
14
+ NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n")), nil),
15
+ NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil),
16
+ NewReaderFile("boop.txt", ioutil.NopCloser(strings.NewReader("boop")), nil),
17
}
18
buf := make([]byte, 20)
19
20
- sf := &SliceFile{name, files}
20
+ sf := NewSliceFile(name, files)
21
22
if !sf.IsDirectory() {
23
t.Error("SliceFile should always be a directory")
@@ -55,7 +55,7 @@ func TestSliceFiles(t *testing.T) {
55
56
func TestReaderFiles(t *testing.T) {
57
message := "beep boop"
58
- rf := &ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader(message))}
58
+ rf := NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader(message)), nil)
59
buf := make([]byte, len(message))
60
61
if rf.IsDirectory() {
commands/files/readerfile.go
+18
-6
@@ -1,12 +1,20 @@
1
package files
2
3
-import "io"
3
+import (
4
+ "io"
5
+ "os"
6
+)
7
8
// ReaderFile is a implementation of File created from an `io.Reader`.
9
// ReaderFiles are never directories, and can be read from and closed.
10
type ReaderFile struct {
8
- Filename string
9
- Reader io.ReadCloser
11
+ filename string
12
+ reader io.ReadCloser
13
+ stat os.FileInfo
14
+}
15
+
16
+func NewReaderFile(filename string, reader io.ReadCloser, stat os.FileInfo) *ReaderFile {
17
+ return &ReaderFile{filename, reader, stat}
18
}
19
20
func (f *ReaderFile) IsDirectory() bool {
@@ -18,13 +26,17 @@ func (f *ReaderFile) NextFile() (File, error) {
26
}
27
28
func (f *ReaderFile) FileName() string {
21
- return f.Filename
29
+ return f.filename
30
}
31
32
func (f *ReaderFile) Read(p []byte) (int, error) {
25
- return f.Reader.Read(p)
33
+ return f.reader.Read(p)
34
}
35
36
func (f *ReaderFile) Close() error {
29
- return f.Reader.Close()
37
+ return f.reader.Close()
38
+}
39
+
40
+func (f *ReaderFile) Stat() os.FileInfo {
41
+ return f.stat
42
}
commands/files/serialfile.go
+7
-2
@@ -20,6 +20,7 @@ func (es sortFIByName) Less(i, j int) bool { return es[i].Name() < es[j].Name()
20
type serialFile struct {
21
path string
22
files []os.FileInfo
23
+ stat os.FileInfo
24
current *os.File
25
}
26
@@ -35,7 +36,7 @@ func NewSerialFile(path string, file *os.File) (File, error) {
36
func newSerialFile(path string, file *os.File, stat os.FileInfo) (File, error) {
37
// for non-directories, return a ReaderFile
38
if !stat.IsDir() {
38
- return &ReaderFile{path, file}, nil
39
+ return &ReaderFile{path, file, stat}, nil
40
}
41
42
// 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) {
56
// make sure contents are sorted so -- repeatably -- we get the same inputs.
57
sort.Sort(sortFIByName(contents))
58
58
- return &serialFile{path, contents, nil}, nil
59
+ return &serialFile{path, contents, stat, nil}, nil
60
}
61
62
func (f *serialFile) IsDirectory() bool {
@@ -113,3 +114,7 @@ func (f *serialFile) Close() error {
114
115
return nil
116
}
117
+
118
+func (f *serialFile) Stat() os.FileInfo {
119
+ return f.stat
120
+}
commands/files/slicefile.go
+19
-6
@@ -6,8 +6,13 @@ import "io"
6
// It contains children files, and is created from a `[]File`.
7
// SliceFiles are always directories, and can't be read from or closed.
8
type SliceFile struct {
9
- Filename string
10
- Files []File
9
+ filename string
10
+ files []File
11
+ n int
12
+}
13
+
14
+func NewSliceFile(filename string, files []File) *SliceFile {
15
+ return &SliceFile{filename, files, 0}
16
}
17
18
func (f *SliceFile) IsDirectory() bool {
@@ -15,16 +20,16 @@ func (f *SliceFile) IsDirectory() bool {
20
}
21
22
func (f *SliceFile) NextFile() (File, error) {
18
- if len(f.Files) == 0 {
23
+ if f.n >= len(f.files) {
24
return nil, io.EOF
25
}
21
- file := f.Files[0]
22
- f.Files = f.Files[1:]
26
+ file := f.files[f.n]
27
+ f.n++
28
return file, nil
29
}
30
31
func (f *SliceFile) FileName() string {
27
- return f.Filename
32
+ return f.filename
33
}
34
35
func (f *SliceFile) Read(p []byte) (int, error) {
@@ -34,3 +39,11 @@ func (f *SliceFile) Read(p []byte) (int, error) {
39
func (f *SliceFile) Close() error {
40
return ErrNotReader
41
}
42
+
43
+func (f *SliceFile) Peek(n int) File {
44
+ return f.files[n]
45
+}
46
+
47
+func (f *SliceFile) Length() int {
48
+ return len(f.files)
49
+}
commands/http/multifilereader_test.go
+8
-8
@@ -13,14 +13,14 @@ import (
13
func TestOutput(t *testing.T) {
14
text := "Some text! :)"
15
fileset := []files.File{
16
- &files.ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader(text))},
17
- &files.SliceFile{"boop", []files.File{
18
- &files.ReaderFile{"boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep"))},
19
- &files.ReaderFile{"boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop"))},
20
- }},
21
- &files.ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))},
22
- }
23
- sf := &files.SliceFile{"", fileset}
16
+ files.NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader(text)), nil),
17
+ files.NewSliceFile("boop", []files.File{
18
+ files.NewReaderFile("boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep")), nil),
19
+ files.NewReaderFile("boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop")), nil),
20
+ }),
21
+ files.NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil),
22
+ }
23
+ sf := files.NewSliceFile("", fileset)
24
buf := make([]byte, 20)
25
26
// testing output by reading it with the go stdlib "mime/multipart" Reader