1
2
3
4
5 package fstest
6
7 import (
8 "io"
9 "io/fs"
10 "path"
11 "slices"
12 "strings"
13 "time"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 type MapFS map[string]*MapFile
34
35
36 type MapFile struct {
37 Data []byte
38 Mode fs.FileMode
39 ModTime time.Time
40 Sys any
41 }
42
43 var _ fs.FS = MapFS(nil)
44 var _ fs.File = (*openMapFile)(nil)
45
46
47 func (fsys MapFS) Open(name string) (fs.File, error) {
48 if !fs.ValidPath(name) {
49 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
50 }
51 file := fsys[name]
52 if file != nil && file.Mode&fs.ModeDir == 0 {
53
54 return &openMapFile{name, mapFileInfo{path.Base(name), file}, 0}, nil
55 }
56
57
58
59
60
61 var list []mapFileInfo
62 var elem string
63 var need = make(map[string]bool)
64 if name == "." {
65 elem = "."
66 for fname, f := range fsys {
67 i := strings.Index(fname, "/")
68 if i < 0 {
69 if fname != "." {
70 list = append(list, mapFileInfo{fname, f})
71 }
72 } else {
73 need[fname[:i]] = true
74 }
75 }
76 } else {
77 elem = name[strings.LastIndex(name, "/")+1:]
78 prefix := name + "/"
79 for fname, f := range fsys {
80 if strings.HasPrefix(fname, prefix) {
81 felem := fname[len(prefix):]
82 i := strings.Index(felem, "/")
83 if i < 0 {
84 list = append(list, mapFileInfo{felem, f})
85 } else {
86 need[fname[len(prefix):len(prefix)+i]] = true
87 }
88 }
89 }
90
91
92
93 if file == nil && list == nil && len(need) == 0 {
94 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
95 }
96 }
97 for _, fi := range list {
98 delete(need, fi.name)
99 }
100 for name := range need {
101 list = append(list, mapFileInfo{name, &MapFile{Mode: fs.ModeDir | 0555}})
102 }
103 slices.SortFunc(list, func(a, b mapFileInfo) int {
104 return strings.Compare(a.name, b.name)
105 })
106
107 if file == nil {
108 file = &MapFile{Mode: fs.ModeDir | 0555}
109 }
110 return &mapDir{name, mapFileInfo{elem, file}, list, 0}, nil
111 }
112
113
114
115
116
117
118
119 type fsOnly struct{ fs.FS }
120
121 func (fsys MapFS) ReadFile(name string) ([]byte, error) {
122 return fs.ReadFile(fsOnly{fsys}, name)
123 }
124
125 func (fsys MapFS) Stat(name string) (fs.FileInfo, error) {
126 return fs.Stat(fsOnly{fsys}, name)
127 }
128
129 func (fsys MapFS) ReadDir(name string) ([]fs.DirEntry, error) {
130 return fs.ReadDir(fsOnly{fsys}, name)
131 }
132
133 func (fsys MapFS) Glob(pattern string) ([]string, error) {
134 return fs.Glob(fsOnly{fsys}, pattern)
135 }
136
137 type noSub struct {
138 MapFS
139 }
140
141 func (noSub) Sub() {}
142
143 func (fsys MapFS) Sub(dir string) (fs.FS, error) {
144 return fs.Sub(noSub{fsys}, dir)
145 }
146
147
148 type mapFileInfo struct {
149 name string
150 f *MapFile
151 }
152
153 func (i *mapFileInfo) Name() string { return path.Base(i.name) }
154 func (i *mapFileInfo) Size() int64 { return int64(len(i.f.Data)) }
155 func (i *mapFileInfo) Mode() fs.FileMode { return i.f.Mode }
156 func (i *mapFileInfo) Type() fs.FileMode { return i.f.Mode.Type() }
157 func (i *mapFileInfo) ModTime() time.Time { return i.f.ModTime }
158 func (i *mapFileInfo) IsDir() bool { return i.f.Mode&fs.ModeDir != 0 }
159 func (i *mapFileInfo) Sys() any { return i.f.Sys }
160 func (i *mapFileInfo) Info() (fs.FileInfo, error) { return i, nil }
161
162 func (i *mapFileInfo) String() string {
163 return fs.FormatFileInfo(i)
164 }
165
166
167 type openMapFile struct {
168 path string
169 mapFileInfo
170 offset int64
171 }
172
173 func (f *openMapFile) Stat() (fs.FileInfo, error) { return &f.mapFileInfo, nil }
174
175 func (f *openMapFile) Close() error { return nil }
176
177 func (f *openMapFile) Read(b []byte) (int, error) {
178 if f.offset >= int64(len(f.f.Data)) {
179 return 0, io.EOF
180 }
181 if f.offset < 0 {
182 return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
183 }
184 n := copy(b, f.f.Data[f.offset:])
185 f.offset += int64(n)
186 return n, nil
187 }
188
189 func (f *openMapFile) Seek(offset int64, whence int) (int64, error) {
190 switch whence {
191 case 0:
192
193 case 1:
194 offset += f.offset
195 case 2:
196 offset += int64(len(f.f.Data))
197 }
198 if offset < 0 || offset > int64(len(f.f.Data)) {
199 return 0, &fs.PathError{Op: "seek", Path: f.path, Err: fs.ErrInvalid}
200 }
201 f.offset = offset
202 return offset, nil
203 }
204
205 func (f *openMapFile) ReadAt(b []byte, offset int64) (int, error) {
206 if offset < 0 || offset > int64(len(f.f.Data)) {
207 return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
208 }
209 n := copy(b, f.f.Data[offset:])
210 if n < len(b) {
211 return n, io.EOF
212 }
213 return n, nil
214 }
215
216
217 type mapDir struct {
218 path string
219 mapFileInfo
220 entry []mapFileInfo
221 offset int
222 }
223
224 func (d *mapDir) Stat() (fs.FileInfo, error) { return &d.mapFileInfo, nil }
225 func (d *mapDir) Close() error { return nil }
226 func (d *mapDir) Read(b []byte) (int, error) {
227 return 0, &fs.PathError{Op: "read", Path: d.path, Err: fs.ErrInvalid}
228 }
229
230 func (d *mapDir) ReadDir(count int) ([]fs.DirEntry, error) {
231 n := len(d.entry) - d.offset
232 if n == 0 && count > 0 {
233 return nil, io.EOF
234 }
235 if count > 0 && n > count {
236 n = count
237 }
238 list := make([]fs.DirEntry, n)
239 for i := range list {
240 list[i] = &d.entry[d.offset+i]
241 }
242 d.offset += n
243 return list, nil
244 }
245
View as plain text