-
Notifications
You must be signed in to change notification settings - Fork 19
/
files.go
132 lines (117 loc) · 2.88 KB
/
files.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
// Copyright 2020 The Scriggo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package scriggo
import (
"io"
"io/fs"
"os"
"path"
"sort"
"strings"
"time"
)
// Files implements a file system that read the files from a map.
type Files map[string][]byte
// Open opens the named file.
func (fsys Files) Open(name string) (fs.File, error) {
if fs.ValidPath(name) {
if name == "." {
return &filesDir{filesFile: filesFile{name: name, mode: fs.ModeDir}, fsys: fsys}, nil
}
data, ok := fsys[name]
if ok {
return &filesFile{name, data, 0, 0}, nil
}
prefix := name + "/"
for n := range fsys {
if strings.HasPrefix(n, prefix) {
return &filesDir{filesFile: filesFile{name: name, mode: fs.ModeDir}, fsys: fsys}, nil
}
}
}
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
type filesDir struct {
filesFile
fsys map[string][]byte
n int
}
func (d *filesDir) ReadDir(n int) ([]fs.DirEntry, error) {
var dir string
if d.name != "." {
dir = d.name + "/"
}
var names []string
hasDir := map[string]bool{}
for name := range d.fsys {
if !strings.HasPrefix(name, dir) {
continue
}
if i := strings.IndexByte(name[len(dir):], '/'); i > 0 {
name = name[:len(dir)+i]
if hasDir[name] {
continue
}
hasDir[name] = true
}
names = append(names, name)
}
sort.Strings(names)
if n > 0 {
if len(names) <= d.n {
return nil, io.EOF
}
names = names[d.n:]
if len(names) > n {
names = names[:n]
}
d.n += len(names)
}
entries := make([]fs.DirEntry, len(names))
for i, name := range names {
entries[i] = &filesDirEntry{filesFileInfo{name: name}}
}
return entries, nil
}
// filesDirEntry implements fs.DirEntry.
type filesDirEntry struct {
filesFileInfo
}
func (f *filesDirEntry) Type() fs.FileMode {
return f.Mode()
}
func (f *filesDirEntry) Info() (fs.FileInfo, error) {
return &f.filesFileInfo, nil
}
type filesFile struct {
name string
data []byte
offset int
mode os.FileMode
}
func (f *filesFile) Stat() (os.FileInfo, error) {
return (*filesFileInfo)(f), nil
}
func (f *filesFile) Read(p []byte) (int, error) {
if f.offset < 0 {
return 0, &os.PathError{Op: "read", Path: f.name, Err: os.ErrInvalid}
}
if f.offset == len(f.data) {
return 0, io.EOF
}
n := copy(p, f.data[f.offset:])
f.offset += n
return n, nil
}
func (f *filesFile) Close() error {
f.offset = -1
return nil
}
type filesFileInfo filesFile
func (i *filesFileInfo) Name() string { return path.Base(i.name) }
func (i *filesFileInfo) Size() int64 { return int64(len(i.data)) }
func (i *filesFileInfo) Mode() os.FileMode { return i.mode }
func (i *filesFileInfo) ModTime() time.Time { return time.Time{} }
func (i *filesFileInfo) IsDir() bool { return i.mode&fs.ModeDir == fs.ModeDir }
func (i *filesFileInfo) Sys() interface{} { return nil }