github.com/v2fly/tools@v0.100.0/godoc/vfs/mapfs/mapfs.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package mapfs file provides an implementation of the FileSystem 6 // interface based on the contents of a map[string]string. 7 package mapfs // import "github.com/v2fly/tools/godoc/vfs/mapfs" 8 9 import ( 10 "fmt" 11 "io" 12 "os" 13 pathpkg "path" 14 "sort" 15 "strings" 16 "time" 17 18 "github.com/v2fly/tools/godoc/vfs" 19 ) 20 21 // New returns a new FileSystem from the provided map. 22 // Map keys must be forward slash-separated paths with 23 // no leading slash, such as "file1.txt" or "dir/file2.txt". 24 // New panics if any of the paths contain a leading slash. 25 func New(m map[string]string) vfs.FileSystem { 26 // Verify all provided paths are relative before proceeding. 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 // mapFS is the map based implementation of FileSystem 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 // slashdir returns path.Dir(p), but special-cases paths not beginning 88 // with a slash to be in the root. 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) // base -> fi 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 // mapFI is the map-based implementation of FileInfo. 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 }