-
Notifications
You must be signed in to change notification settings - Fork 17
/
fs.go
174 lines (154 loc) · 4.86 KB
/
fs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package memoryfs
import (
"io"
"io/fs"
"io/ioutil"
"strings"
"time"
)
// FS is an in-memory filesystem
type FS struct {
dir *dir
}
// New creates a new filesystem
func New() *FS {
return &FS{
dir: &dir{
info: fileinfo{
name: ".",
size: 0x100,
modified: time.Now(),
mode: 0o0700 | fs.ModeDir,
},
dirs: map[string]*dir{},
files: map[string]*file{},
},
}
}
// CloneFS allows you to take on fs.FS and wrap it in an fs that is writable
func CloneFS(base fs.FS) *FS {
newFS := New()
fs.WalkDir(base, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return newFS.MkdirAll(path, d.Type().Perm())
}
// Lazy write the files, holding onto the base FS to read the content on demand
return newFS.WriteLazyFile(path, func() (io.Reader, error) {
return base.Open(path)
}, d.Type().Perm())
})
return newFS
}
// Stat returns a FileInfo describing the file.
func (m *FS) Stat(name string) (fs.FileInfo, error) {
name = cleanse(name)
if f, err := m.dir.getFile(name); err == nil {
return f.stat(), nil
}
if f, err := m.dir.getDir(name); err == nil {
return f.Stat()
}
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrNotExist}
}
// ReadDir reads the named directory
// and returns a list of directory entries sorted by filename.
func (m *FS) ReadDir(name string) ([]fs.DirEntry, error) {
return m.dir.ReadDir(cleanse(name))
}
// Open opens the named file for reading.
func (m *FS) Open(name string) (fs.File, error) {
return m.dir.Open(cleanse(name))
}
// WriteFile writes the specified bytes to the named file. If the file exists, it will be overwritten.
func (m *FS) WriteFile(path string, data []byte, perm fs.FileMode) error {
return m.dir.WriteFile(cleanse(path), data, perm)
}
// MkdirAll creates a directory named path,
// along with any necessary parents, and returns nil,
// or else returns an error.
// The permission bits perm (before umask) are used for all
// directories that MkdirAll creates.
// If path is already a directory, MkdirAll does nothing
// and returns nil.
func (m *FS) MkdirAll(path string, perm fs.FileMode) error {
return m.dir.MkdirAll(cleanse(path), perm)
}
// ReadFile reads the named file and returns its contents.
// A successful call returns a nil error, not io.EOF.
// (Because ReadFile reads the whole file, the expected EOF
// from the final Read is not treated as an error to be reported.)
//
// The caller is permitted to modify the returned byte slice.
// This method should return a copy of the underlying data.
func (m *FS) ReadFile(name string) ([]byte, error) {
f, err := m.dir.Open(cleanse(name))
if err != nil {
return nil, err
}
defer func() { _ = f.Close() }()
return ioutil.ReadAll(f)
}
// Sub returns an FS corresponding to the subtree rooted at dir.
func (m *FS) Sub(dir string) (fs.FS, error) {
d, err := m.dir.getDir(cleanse(dir))
if err != nil {
return nil, err
}
return &FS{
dir: d,
}, nil
}
// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
func (m *FS) Glob(pattern string) ([]string, error) {
pattern = strings.ReplaceAll(pattern, "/", separator)
return m.dir.glob(pattern)
}
// WriteLazyFile creates (or overwrites) the named file.
// The contents of the file are not set at this time, but are read on-demand later using the provided LazyOpener.
func (m *FS) WriteLazyFile(path string, opener LazyOpener, perm fs.FileMode) error {
return m.dir.WriteLazyFile(cleanse(path), opener, perm)
}
// Remove deletes a file or directory from the filesystem
func (m *FS) Remove(path string) error {
return m.dir.Remove(cleanse(path))
}
// RemoveAll deletes a file or directory and any children if present from the filesystem
func (m *FS) RemoveAll(path string) error {
return m.dir.RemoveAll(cleanse(path))
}
// SetModified set modified time to file or directory
func (m *FS) SetModified(name string, modified time.Time) error {
name = cleanse(name)
if f, err := m.dir.getFile(name); err == nil {
f.info.modified = modified
return nil
}
if f, err := m.dir.getDir(name); err == nil {
f.info.modified = modified
return nil
}
return &fs.PathError{Op: "set modified", Path: name, Err: fs.ErrNotExist}
}
// SetSys set underlying data source to file or directory
func (m *FS) SetSys(name string, sys interface{}) error {
name = cleanse(name)
if f, err := m.dir.getFile(name); err == nil {
f.info.sys = sys
return nil
}
if f, err := m.dir.getDir(name); err == nil {
f.info.sys = sys
return nil
}
return &fs.PathError{Op: "set sys", Path: name, Err: fs.ErrNotExist}
}