13 // serialFile implements Node, and reads from a path on the OS filesystem.
14 // No more than one file will be opened at a time (directories will advance
15 // to the next file when NextFile() is called).
16 type serialFile struct {
20 handleHiddenFiles bool
23 type serialIterator struct {
25 handleHiddenFiles bool
34 // TODO: test/document limitations
35 func NewSerialFile(path string, hidden bool, stat os.FileInfo) (Node, error) {
36 switch mode := stat.Mode(); {
37 case mode.IsRegular():
38 file, err := os.Open(path)
42 return NewReaderPathFile(path, file, stat)
44 // for directories, stat all of the contents first, so we know what files to
45 // open when NextFile() is called
46 contents, err := ioutil.ReadDir(path)
50 return &serialFile{path, contents, stat, hidden}, nil
51 case mode&os.ModeSymlink != 0:
52 target, err := os.Readlink(path)
56 return NewLinkFile(target, stat), nil
58 return nil, fmt.Errorf("unrecognized file type for %s: %s", path, mode.String())
62 func (it *serialIterator) Name() string {
66 func (it *serialIterator) Node() Node {
70 func (it *serialIterator) Next() bool {
71 // if there aren't any files left in the root directory, we're done
72 if len(it.files) == 0 {
77 it.files = it.files[1:]
78 for !it.handleHiddenFiles && strings.HasPrefix(stat.Name(), ".") {
79 if len(it.files) == 0 {
84 it.files = it.files[1:]
88 filePath := filepath.ToSlash(filepath.Join(it.path, stat.Name()))
90 // recursively call the constructor on the next file
91 // if it's a regular file, we will open it as a ReaderFile
92 // if it's a directory, files in it will be opened serially
93 sf, err := NewSerialFile(filePath, it.handleHiddenFiles, stat)
99 it.curName = stat.Name()
104 func (it *serialIterator) Err() error {
108 func (f *serialFile) Entries() DirIterator {
109 return &serialIterator{
112 handleHiddenFiles: f.handleHiddenFiles,
116 func (f *serialFile) NextFile() (string, Node, error) {
117 // if there aren't any files left in the root directory, we're done
118 if len(f.files) == 0 {
119 return "", nil, io.EOF
123 f.files = f.files[1:]
125 for !f.handleHiddenFiles && strings.HasPrefix(stat.Name(), ".") {
126 if len(f.files) == 0 {
127 return "", nil, io.EOF
131 f.files = f.files[1:]
134 // open the next file
135 filePath := filepath.ToSlash(filepath.Join(f.path, stat.Name()))
137 // recursively call the constructor on the next file
138 // if it's a regular file, we will open it as a ReaderFile
139 // if it's a directory, files in it will be opened serially
140 sf, err := NewSerialFile(filePath, f.handleHiddenFiles, stat)
145 return stat.Name(), sf, nil
148 func (f *serialFile) Close() error {
152 func (f *serialFile) Stat() os.FileInfo {
156 func (f *serialFile) Size() (int64, error) {
158 //something went terribly, terribly wrong
159 return 0, errors.New("serialFile is not a directory")
163 err := filepath.Walk(f.path, func(p string, fi os.FileInfo, err error) error {
168 if fi != nil && fi.Mode().IsRegular() {
177 var _ Directory = &serialFile{}
178 var _ DirIterator = &serialIterator{}