Commit 8ca8d6ef authored by Matt Bell's avatar Matt Bell

commands/files: Added PeekFile and StatFile interfaces

squash! commands/files: Added PeekFile and StatFile interfaces

commands/http: Updated test
parent 487ef33e
...@@ -64,7 +64,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c ...@@ -64,7 +64,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
} }
req.SetArguments(stringArgs) req.SetArguments(stringArgs)
file := &files.SliceFile{"", fileArgs} file := files.NewSliceFile("", fileArgs)
req.SetFiles(file) req.SetFiles(file)
err = cmd.CheckArguments(req) err = cmd.CheckArguments(req)
...@@ -298,7 +298,7 @@ func appendFile(args []files.File, inputs []string, argDef *cmds.Argument, recur ...@@ -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) { 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 return append(args, arg), nil
} }
......
...@@ -3,6 +3,7 @@ package files ...@@ -3,6 +3,7 @@ package files
import ( import (
"errors" "errors"
"io" "io"
"os"
) )
var ( var (
...@@ -29,3 +30,16 @@ type File interface { ...@@ -29,3 +30,16 @@ type File interface {
// If the file is a regular file (not a directory), NextFile will return a non-nil error. // If the file is a regular file (not a directory), NextFile will return a non-nil error.
NextFile() (File, error) NextFile() (File, error)
} }
type StatFile interface {
File
Stat() os.FileInfo
}
type PeekFile interface {
File
Peek(n int) File
Length() int
}
...@@ -11,13 +11,13 @@ import ( ...@@ -11,13 +11,13 @@ import (
func TestSliceFiles(t *testing.T) { func TestSliceFiles(t *testing.T) {
name := "testname" name := "testname"
files := []File{ files := []File{
&ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n"))}, NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader("Some text!\n")), nil),
&ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))}, NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil),
&ReaderFile{"boop.txt", ioutil.NopCloser(strings.NewReader("boop"))}, NewReaderFile("boop.txt", ioutil.NopCloser(strings.NewReader("boop")), nil),
} }
buf := make([]byte, 20) buf := make([]byte, 20)
sf := &SliceFile{name, files} sf := NewSliceFile(name, files)
if !sf.IsDirectory() { if !sf.IsDirectory() {
t.Error("SliceFile should always be a directory") t.Error("SliceFile should always be a directory")
...@@ -55,7 +55,7 @@ func TestSliceFiles(t *testing.T) { ...@@ -55,7 +55,7 @@ func TestSliceFiles(t *testing.T) {
func TestReaderFiles(t *testing.T) { func TestReaderFiles(t *testing.T) {
message := "beep boop" 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)) buf := make([]byte, len(message))
if rf.IsDirectory() { if rf.IsDirectory() {
......
package files package files
import "io" import (
"io"
"os"
)
// ReaderFile is a implementation of File created from an `io.Reader`. // ReaderFile is a implementation of File created from an `io.Reader`.
// ReaderFiles are never directories, and can be read from and closed. // ReaderFiles are never directories, and can be read from and closed.
type ReaderFile struct { type ReaderFile struct {
Filename string filename string
Reader io.ReadCloser 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 { func (f *ReaderFile) IsDirectory() bool {
...@@ -18,13 +26,17 @@ func (f *ReaderFile) NextFile() (File, error) { ...@@ -18,13 +26,17 @@ func (f *ReaderFile) NextFile() (File, error) {
} }
func (f *ReaderFile) FileName() string { func (f *ReaderFile) FileName() string {
return f.Filename return f.filename
} }
func (f *ReaderFile) Read(p []byte) (int, error) { func (f *ReaderFile) Read(p []byte) (int, error) {
return f.Reader.Read(p) return f.reader.Read(p)
} }
func (f *ReaderFile) Close() error { func (f *ReaderFile) Close() error {
return f.Reader.Close() return f.reader.Close()
}
func (f *ReaderFile) Stat() os.FileInfo {
return f.stat
} }
...@@ -20,6 +20,7 @@ func (es sortFIByName) Less(i, j int) bool { return es[i].Name() < es[j].Name() ...@@ -20,6 +20,7 @@ func (es sortFIByName) Less(i, j int) bool { return es[i].Name() < es[j].Name()
type serialFile struct { type serialFile struct {
path string path string
files []os.FileInfo files []os.FileInfo
stat os.FileInfo
current *os.File current *os.File
} }
...@@ -35,7 +36,7 @@ func NewSerialFile(path string, file *os.File) (File, error) { ...@@ -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) { func newSerialFile(path string, file *os.File, stat os.FileInfo) (File, error) {
// for non-directories, return a ReaderFile // for non-directories, return a ReaderFile
if !stat.IsDir() { 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 // 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) { ...@@ -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. // make sure contents are sorted so -- repeatably -- we get the same inputs.
sort.Sort(sortFIByName(contents)) sort.Sort(sortFIByName(contents))
return &serialFile{path, contents, nil}, nil return &serialFile{path, contents, stat, nil}, nil
} }
func (f *serialFile) IsDirectory() bool { func (f *serialFile) IsDirectory() bool {
...@@ -113,3 +114,7 @@ func (f *serialFile) Close() error { ...@@ -113,3 +114,7 @@ func (f *serialFile) Close() error {
return nil return nil
} }
func (f *serialFile) Stat() os.FileInfo {
return f.stat
}
...@@ -6,8 +6,13 @@ import "io" ...@@ -6,8 +6,13 @@ import "io"
// It contains children files, and is created from a `[]File`. // It contains children files, and is created from a `[]File`.
// SliceFiles are always directories, and can't be read from or closed. // SliceFiles are always directories, and can't be read from or closed.
type SliceFile struct { type SliceFile struct {
Filename string filename string
Files []File files []File
n int
}
func NewSliceFile(filename string, files []File) *SliceFile {
return &SliceFile{filename, files, 0}
} }
func (f *SliceFile) IsDirectory() bool { func (f *SliceFile) IsDirectory() bool {
...@@ -15,16 +20,16 @@ func (f *SliceFile) IsDirectory() bool { ...@@ -15,16 +20,16 @@ func (f *SliceFile) IsDirectory() bool {
} }
func (f *SliceFile) NextFile() (File, error) { func (f *SliceFile) NextFile() (File, error) {
if len(f.Files) == 0 { if f.n >= len(f.files) {
return nil, io.EOF return nil, io.EOF
} }
file := f.Files[0] file := f.files[f.n]
f.Files = f.Files[1:] f.n++
return file, nil return file, nil
} }
func (f *SliceFile) FileName() string { func (f *SliceFile) FileName() string {
return f.Filename return f.filename
} }
func (f *SliceFile) Read(p []byte) (int, error) { func (f *SliceFile) Read(p []byte) (int, error) {
...@@ -34,3 +39,11 @@ 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 { func (f *SliceFile) Close() error {
return ErrNotReader return ErrNotReader
} }
func (f *SliceFile) Peek(n int) File {
return f.files[n]
}
func (f *SliceFile) Length() int {
return len(f.files)
}
...@@ -13,14 +13,14 @@ import ( ...@@ -13,14 +13,14 @@ import (
func TestOutput(t *testing.T) { func TestOutput(t *testing.T) {
text := "Some text! :)" text := "Some text! :)"
fileset := []files.File{ fileset := []files.File{
&files.ReaderFile{"file.txt", ioutil.NopCloser(strings.NewReader(text))}, files.NewReaderFile("file.txt", ioutil.NopCloser(strings.NewReader(text)), nil),
&files.SliceFile{"boop", []files.File{ files.NewSliceFile("boop", []files.File{
&files.ReaderFile{"boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep"))}, files.NewReaderFile("boop/a.txt", ioutil.NopCloser(strings.NewReader("bleep")), nil),
&files.ReaderFile{"boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop"))}, files.NewReaderFile("boop/b.txt", ioutil.NopCloser(strings.NewReader("bloop")), nil),
}}, }),
&files.ReaderFile{"beep.txt", ioutil.NopCloser(strings.NewReader("beep"))}, files.NewReaderFile("beep.txt", ioutil.NopCloser(strings.NewReader("beep")), nil),
} }
sf := &files.SliceFile{"", fileset} sf := files.NewSliceFile("", fileset)
buf := make([]byte, 20) buf := make([]byte, 20)
// testing output by reading it with the go stdlib "mime/multipart" Reader // testing output by reading it with the go stdlib "mime/multipart" Reader
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment