-
Notifications
You must be signed in to change notification settings - Fork 3
/
memfs.go
137 lines (115 loc) · 2.89 KB
/
memfs.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
package u
import (
"io"
"io/fs"
"strings"
"time"
)
// MemoryFS is a custom file system that uses a map to store file data.
type MemoryFS struct {
m map[string][]byte
modTime time.Time
}
func NewMemoryFS(m map[string][]byte) *MemoryFS {
return &MemoryFS{
m: m,
modTime: time.Now(),
}
}
func NewMemoryFSForZipData(zipData []byte) (*MemoryFS, error) {
m, err := ReadZipData(zipData)
if err != nil {
return nil, err
}
return NewMemoryFS(m), nil
}
// Open implements the fs.FS interface for MemoryFS.
func (m MemoryFS) Open(name string) (fs.File, error) {
data, exists := m.m[name]
if !exists {
return nil, fs.ErrNotExist
}
return &memoryFile{name: name, data: data, modTime: m.modTime}, nil
}
// optimization for fs.ReadFile https://pkg.go.dev/io/fs#ReadFile
func (m MemoryFS) ReadFile(name string) ([]byte, error) {
data, exists := m.m[name]
if !exists {
return nil, fs.ErrNotExist
}
return data, nil
}
// memoryFile is a custom type that satisfies the fs.File interface.
type memoryFile struct {
name string
data []byte
off int
modTime time.Time
}
// Read implements the fs.File interface for memoryFile.
func (f *memoryFile) Read(b []byte) (int, error) {
if f.off >= len(f.data) {
return 0, io.EOF
}
n := copy(b, f.data[f.off:])
f.off += n
return n, nil
}
// Close implements the fs.File interface for memoryFile.
func (f *memoryFile) Close() error {
return nil
}
// Stat implements the fs.File interface for memoryFile.
func (f *memoryFile) Stat() (fs.FileInfo, error) {
return fileInfo{name: f.name, size: int64(len(f.data)), modTime: f.modTime}, nil
}
// fileInfo is a custom type that satisfies the fs.FileInfo interface.
type fileInfo struct {
name string
size int64
modTime time.Time
}
// Name returns the name of the file.
func (fi fileInfo) Name() string {
return fi.name
}
// Size returns the size of the file.
func (fi fileInfo) Size() int64 {
return fi.size
}
// Mode returns the file mode (always regular file).
func (fi fileInfo) Mode() fs.FileMode {
return 0
}
// ModTime returns the modification time (not implemented).
func (fi fileInfo) ModTime() time.Time {
return fi.modTime
}
// IsDir returns false (always a regular file).
func (fi fileInfo) IsDir() bool {
return false
}
// Sys returns nil (no system info available).
func (fi fileInfo) Sys() interface{} {
return nil
}
func FsFileExists(fsys fs.FS, path string) bool {
// paths must use '/' (unix) not '\' (windows)
PanicIf(strings.Contains(path, "\\"))
if mfs, ok := fsys.(*MemoryFS); ok {
return mfs.m[path] != nil
}
if fstat, ok := fsys.(fs.StatFS); ok {
st, err := fstat.Stat(path)
if err != nil {
return false
}
return !st.IsDir()
}
f, err := fsys.Open(path)
if err != nil {
return false
}
f.Close()
return true
}