...
1
2
3
4
5
6
7 package mapfs
8
9 import (
10 "fmt"
11 "io"
12 "os"
13 pathpkg "path"
14 "sort"
15 "strings"
16 "time"
17
18 "golang.org/x/tools/godoc/vfs"
19 )
20
21
22
23
24
25 func New(m map[string]string) vfs.FileSystem {
26
27 var pathsWithLeadingSlash []string
28 for p := range m {
29 if strings.HasPrefix(p, "/") {
30 pathsWithLeadingSlash = append(pathsWithLeadingSlash, p)
31 }
32 }
33 if len(pathsWithLeadingSlash) > 0 {
34 panic(fmt.Errorf("mapfs.New: invalid paths with a leading slash: %q", pathsWithLeadingSlash))
35 }
36
37 return mapFS(m)
38 }
39
40
41 type mapFS map[string]string
42
43 func (fs mapFS) String() string { return "mapfs" }
44
45 func (fs mapFS) RootType(p string) vfs.RootType {
46 return ""
47 }
48
49 func (fs mapFS) Close() error { return nil }
50
51 func filename(p string) string {
52 return strings.TrimPrefix(p, "/")
53 }
54
55 func (fs mapFS) Open(p string) (vfs.ReadSeekCloser, error) {
56 b, ok := fs[filename(p)]
57 if !ok {
58 return nil, os.ErrNotExist
59 }
60 return nopCloser{strings.NewReader(b)}, nil
61 }
62
63 func fileInfo(name, contents string) os.FileInfo {
64 return mapFI{name: pathpkg.Base(name), size: len(contents)}
65 }
66
67 func dirInfo(name string) os.FileInfo {
68 return mapFI{name: pathpkg.Base(name), dir: true}
69 }
70
71 func (fs mapFS) Lstat(p string) (os.FileInfo, error) {
72 b, ok := fs[filename(p)]
73 if ok {
74 return fileInfo(p, b), nil
75 }
76 ents, _ := fs.ReadDir(p)
77 if len(ents) > 0 {
78 return dirInfo(p), nil
79 }
80 return nil, os.ErrNotExist
81 }
82
83 func (fs mapFS) Stat(p string) (os.FileInfo, error) {
84 return fs.Lstat(p)
85 }
86
87
88
89 func slashdir(p string) string {
90 d := pathpkg.Dir(p)
91 if d == "." {
92 return "/"
93 }
94 if strings.HasPrefix(p, "/") {
95 return d
96 }
97 return "/" + d
98 }
99
100 func (fs mapFS) ReadDir(p string) ([]os.FileInfo, error) {
101 p = pathpkg.Clean(p)
102 var ents []string
103 fim := make(map[string]os.FileInfo)
104 for fn, b := range fs {
105 dir := slashdir(fn)
106 isFile := true
107 var lastBase string
108 for {
109 if dir == p {
110 base := lastBase
111 if isFile {
112 base = pathpkg.Base(fn)
113 }
114 if fim[base] == nil {
115 var fi os.FileInfo
116 if isFile {
117 fi = fileInfo(fn, b)
118 } else {
119 fi = dirInfo(base)
120 }
121 ents = append(ents, base)
122 fim[base] = fi
123 }
124 }
125 if dir == "/" {
126 break
127 } else {
128 isFile = false
129 lastBase = pathpkg.Base(dir)
130 dir = pathpkg.Dir(dir)
131 }
132 }
133 }
134 if len(ents) == 0 {
135 return nil, os.ErrNotExist
136 }
137
138 sort.Strings(ents)
139 var list []os.FileInfo
140 for _, dir := range ents {
141 list = append(list, fim[dir])
142 }
143 return list, nil
144 }
145
146
147 type mapFI struct {
148 name string
149 size int
150 dir bool
151 }
152
153 func (fi mapFI) IsDir() bool { return fi.dir }
154 func (fi mapFI) ModTime() time.Time { return time.Time{} }
155 func (fi mapFI) Mode() os.FileMode {
156 if fi.IsDir() {
157 return 0755 | os.ModeDir
158 }
159 return 0444
160 }
161 func (fi mapFI) Name() string { return pathpkg.Base(fi.name) }
162 func (fi mapFI) Size() int64 { return int64(fi.size) }
163 func (fi mapFI) Sys() interface{} { return nil }
164
165 type nopCloser struct {
166 io.ReadSeeker
167 }
168
169 func (nc nopCloser) Close() error { return nil }
170
View as plain text