OSDN Git Service

add package
[bytom/vapor.git] / vendor / github.com / ipfs / go-ipfs-files / serialfile.go
1 package files
2
3 import (
4         "errors"
5         "fmt"
6         "io"
7         "io/ioutil"
8         "os"
9         "path/filepath"
10         "strings"
11 )
12
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 {
17         path              string
18         files             []os.FileInfo
19         stat              os.FileInfo
20         handleHiddenFiles bool
21 }
22
23 type serialIterator struct {
24         files             []os.FileInfo
25         handleHiddenFiles bool
26         path              string
27
28         curName string
29         curFile Node
30
31         err error
32 }
33
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)
39                 if err != nil {
40                         return nil, err
41                 }
42                 return NewReaderPathFile(path, file, stat)
43         case mode.IsDir():
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)
47                 if err != nil {
48                         return nil, err
49                 }
50                 return &serialFile{path, contents, stat, hidden}, nil
51         case mode&os.ModeSymlink != 0:
52                 target, err := os.Readlink(path)
53                 if err != nil {
54                         return nil, err
55                 }
56                 return NewLinkFile(target, stat), nil
57         default:
58                 return nil, fmt.Errorf("unrecognized file type for %s: %s", path, mode.String())
59         }
60 }
61
62 func (it *serialIterator) Name() string {
63         return it.curName
64 }
65
66 func (it *serialIterator) Node() Node {
67         return it.curFile
68 }
69
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 {
73                 return false
74         }
75
76         stat := it.files[0]
77         it.files = it.files[1:]
78         for !it.handleHiddenFiles && strings.HasPrefix(stat.Name(), ".") {
79                 if len(it.files) == 0 {
80                         return false
81                 }
82
83                 stat = it.files[0]
84                 it.files = it.files[1:]
85         }
86
87         // open the next file
88         filePath := filepath.ToSlash(filepath.Join(it.path, stat.Name()))
89
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)
94         if err != nil {
95                 it.err = err
96                 return false
97         }
98
99         it.curName = stat.Name()
100         it.curFile = sf
101         return true
102 }
103
104 func (it *serialIterator) Err() error {
105         return it.err
106 }
107
108 func (f *serialFile) Entries() DirIterator {
109         return &serialIterator{
110                 path:              f.path,
111                 files:             f.files,
112                 handleHiddenFiles: f.handleHiddenFiles,
113         }
114 }
115
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
120         }
121
122         stat := f.files[0]
123         f.files = f.files[1:]
124
125         for !f.handleHiddenFiles && strings.HasPrefix(stat.Name(), ".") {
126                 if len(f.files) == 0 {
127                         return "", nil, io.EOF
128                 }
129
130                 stat = f.files[0]
131                 f.files = f.files[1:]
132         }
133
134         // open the next file
135         filePath := filepath.ToSlash(filepath.Join(f.path, stat.Name()))
136
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)
141         if err != nil {
142                 return "", nil, err
143         }
144
145         return stat.Name(), sf, nil
146 }
147
148 func (f *serialFile) Close() error {
149         return nil
150 }
151
152 func (f *serialFile) Stat() os.FileInfo {
153         return f.stat
154 }
155
156 func (f *serialFile) Size() (int64, error) {
157         if !f.stat.IsDir() {
158                 //something went terribly, terribly wrong
159                 return 0, errors.New("serialFile is not a directory")
160         }
161
162         var du int64
163         err := filepath.Walk(f.path, func(p string, fi os.FileInfo, err error) error {
164                 if err != nil {
165                         return err
166                 }
167
168                 if fi != nil && fi.Mode().IsRegular() {
169                         du += fi.Size()
170                 }
171                 return nil
172         })
173
174         return du, err
175 }
176
177 var _ Directory = &serialFile{}
178 var _ DirIterator = &serialIterator{}