2013-08-15 19:44:27 -06:00
|
|
|
// Copyright 2013 The Go 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 mapfs file provides an implementation of the FileSystem
|
|
|
|
// interface based on the contents of a map[string]string.
|
2014-12-08 21:00:58 -07:00
|
|
|
package mapfs // import "golang.org/x/tools/godoc/vfs/mapfs"
|
2013-08-15 19:44:27 -06:00
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"os"
|
2013-11-05 07:35:58 -07:00
|
|
|
pathpkg "path"
|
|
|
|
"sort"
|
2013-08-15 19:44:27 -06:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2014-11-09 14:50:40 -07:00
|
|
|
"golang.org/x/tools/godoc/vfs"
|
2013-08-15 19:44:27 -06:00
|
|
|
)
|
|
|
|
|
2013-11-05 07:35:58 -07:00
|
|
|
// New returns a new FileSystem from the provided map.
|
|
|
|
// Map keys should be forward slash-separated pathnames
|
|
|
|
// and not contain a leading slash.
|
2013-08-15 19:44:27 -06:00
|
|
|
func New(m map[string]string) vfs.FileSystem {
|
|
|
|
return mapFS(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mapFS is the map based implementation of FileSystem
|
|
|
|
type mapFS map[string]string
|
|
|
|
|
|
|
|
func (fs mapFS) String() string { return "mapfs" }
|
|
|
|
|
|
|
|
func (fs mapFS) Close() error { return nil }
|
|
|
|
|
|
|
|
func filename(p string) string {
|
2013-11-05 07:35:58 -07:00
|
|
|
return strings.TrimPrefix(p, "/")
|
2013-08-15 19:44:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (fs mapFS) Open(p string) (vfs.ReadSeekCloser, error) {
|
|
|
|
b, ok := fs[filename(p)]
|
|
|
|
if !ok {
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
return nopCloser{strings.NewReader(b)}, nil
|
|
|
|
}
|
|
|
|
|
2013-11-05 07:35:58 -07:00
|
|
|
func fileInfo(name, contents string) os.FileInfo {
|
|
|
|
return mapFI{name: pathpkg.Base(name), size: len(contents)}
|
|
|
|
}
|
|
|
|
|
|
|
|
func dirInfo(name string) os.FileInfo {
|
|
|
|
return mapFI{name: pathpkg.Base(name), dir: true}
|
|
|
|
}
|
|
|
|
|
2013-08-15 19:44:27 -06:00
|
|
|
func (fs mapFS) Lstat(p string) (os.FileInfo, error) {
|
|
|
|
b, ok := fs[filename(p)]
|
2013-11-05 07:35:58 -07:00
|
|
|
if ok {
|
|
|
|
return fileInfo(p, b), nil
|
|
|
|
}
|
|
|
|
ents, _ := fs.ReadDir(p)
|
|
|
|
if len(ents) > 0 {
|
|
|
|
return dirInfo(p), nil
|
2013-08-15 19:44:27 -06:00
|
|
|
}
|
2013-11-05 07:35:58 -07:00
|
|
|
return nil, os.ErrNotExist
|
2013-08-15 19:44:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (fs mapFS) Stat(p string) (os.FileInfo, error) {
|
|
|
|
return fs.Lstat(p)
|
|
|
|
}
|
|
|
|
|
2013-11-05 07:35:58 -07:00
|
|
|
// slashdir returns path.Dir(p), but special-cases paths not beginning
|
|
|
|
// with a slash to be in the root.
|
|
|
|
func slashdir(p string) string {
|
|
|
|
d := pathpkg.Dir(p)
|
|
|
|
if d == "." {
|
|
|
|
return "/"
|
|
|
|
}
|
|
|
|
if strings.HasPrefix(p, "/") {
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
return "/" + d
|
|
|
|
}
|
|
|
|
|
2013-08-15 19:44:27 -06:00
|
|
|
func (fs mapFS) ReadDir(p string) ([]os.FileInfo, error) {
|
2013-11-05 07:35:58 -07:00
|
|
|
p = pathpkg.Clean(p)
|
|
|
|
var ents []string
|
|
|
|
fim := make(map[string]os.FileInfo) // base -> fi
|
2013-08-15 19:44:27 -06:00
|
|
|
for fn, b := range fs {
|
2013-11-05 07:35:58 -07:00
|
|
|
dir := slashdir(fn)
|
|
|
|
isFile := true
|
|
|
|
var lastBase string
|
|
|
|
for {
|
|
|
|
if dir == p {
|
|
|
|
base := lastBase
|
|
|
|
if isFile {
|
|
|
|
base = pathpkg.Base(fn)
|
|
|
|
}
|
|
|
|
if fim[base] == nil {
|
|
|
|
var fi os.FileInfo
|
|
|
|
if isFile {
|
|
|
|
fi = fileInfo(fn, b)
|
|
|
|
} else {
|
|
|
|
fi = dirInfo(base)
|
|
|
|
}
|
|
|
|
ents = append(ents, base)
|
|
|
|
fim[base] = fi
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dir == "/" {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
isFile = false
|
|
|
|
lastBase = pathpkg.Base(dir)
|
|
|
|
dir = pathpkg.Dir(dir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(ents) == 0 {
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(ents)
|
|
|
|
var list []os.FileInfo
|
|
|
|
for _, dir := range ents {
|
|
|
|
list = append(list, fim[dir])
|
2013-08-15 19:44:27 -06:00
|
|
|
}
|
|
|
|
return list, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// mapFI is the map-based implementation of FileInfo.
|
|
|
|
type mapFI struct {
|
|
|
|
name string
|
2013-11-05 07:35:58 -07:00
|
|
|
size int
|
|
|
|
dir bool
|
2013-08-15 19:44:27 -06:00
|
|
|
}
|
|
|
|
|
2013-11-05 07:35:58 -07:00
|
|
|
func (fi mapFI) IsDir() bool { return fi.dir }
|
2013-08-15 19:44:27 -06:00
|
|
|
func (fi mapFI) ModTime() time.Time { return time.Time{} }
|
2013-11-05 07:35:58 -07:00
|
|
|
func (fi mapFI) Mode() os.FileMode {
|
|
|
|
if fi.IsDir() {
|
|
|
|
return 0755 | os.ModeDir
|
|
|
|
}
|
|
|
|
return 0444
|
|
|
|
}
|
|
|
|
func (fi mapFI) Name() string { return pathpkg.Base(fi.name) }
|
|
|
|
func (fi mapFI) Size() int64 { return int64(fi.size) }
|
|
|
|
func (fi mapFI) Sys() interface{} { return nil }
|
2013-08-15 19:44:27 -06:00
|
|
|
|
|
|
|
type nopCloser struct {
|
|
|
|
io.ReadSeeker
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc nopCloser) Close() error { return nil }
|