2020-09-27 19:23:47 +00:00
|
|
|
package fs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2020-11-01 11:23:39 +00:00
|
|
|
const separator = "/"
|
|
|
|
|
2020-09-27 19:23:47 +00:00
|
|
|
type FsFactory func(f File) (Filesystem, error)
|
|
|
|
|
|
|
|
var SupportedFactories = map[string]FsFactory{
|
|
|
|
".zip": func(f File) (Filesystem, error) {
|
|
|
|
return NewZip(f, f.Size()), nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type storage struct {
|
|
|
|
factories map[string]FsFactory
|
|
|
|
|
|
|
|
files map[string]File
|
|
|
|
filesystems map[string]Filesystem
|
|
|
|
children map[string]map[string]File
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStorage(factories map[string]FsFactory) *storage {
|
|
|
|
return &storage{
|
2021-11-16 12:13:58 +00:00
|
|
|
files: make(map[string]File),
|
|
|
|
children: make(map[string]map[string]File),
|
|
|
|
filesystems: make(map[string]Filesystem),
|
2020-09-27 19:23:47 +00:00
|
|
|
factories: factories,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
func (s *storage) Clear() {
|
|
|
|
s.files = make(map[string]File)
|
|
|
|
s.children = make(map[string]map[string]File)
|
|
|
|
s.filesystems = make(map[string]Filesystem)
|
|
|
|
}
|
|
|
|
|
2020-09-27 19:23:47 +00:00
|
|
|
func (s *storage) Has(path string) bool {
|
|
|
|
path = clean(path)
|
|
|
|
|
|
|
|
f := s.files[path]
|
|
|
|
if f != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if f, _ := s.getFileFromFs(path); f != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-04-04 17:24:58 +00:00
|
|
|
func (s *storage) AddFS(fs Filesystem, p string) error {
|
|
|
|
p = clean(p)
|
|
|
|
if s.Has(p) {
|
|
|
|
if dir, err := s.Get(p); err == nil {
|
|
|
|
if !dir.IsDir() {
|
|
|
|
return os.ErrExist
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
s.filesystems[p] = fs
|
|
|
|
return s.createParent(p, &Dir{})
|
|
|
|
}
|
|
|
|
|
2020-09-27 19:23:47 +00:00
|
|
|
func (s *storage) Add(f File, p string) error {
|
|
|
|
p = clean(p)
|
|
|
|
if s.Has(p) {
|
|
|
|
if dir, err := s.Get(p); err == nil {
|
|
|
|
if !dir.IsDir() {
|
|
|
|
return os.ErrExist
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ext := path.Ext(p)
|
|
|
|
if ffs := s.factories[ext]; ffs != nil {
|
|
|
|
fs, err := ffs(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.filesystems[p] = fs
|
|
|
|
} else {
|
|
|
|
s.files[p] = f
|
|
|
|
}
|
|
|
|
|
2021-04-04 17:24:58 +00:00
|
|
|
return s.createParent(p, f)
|
2020-09-27 19:23:47 +00:00
|
|
|
}
|
|
|
|
|
2020-11-01 11:23:39 +00:00
|
|
|
func (s *storage) createParent(p string, f File) error {
|
|
|
|
base, filename := path.Split(p)
|
2020-09-27 19:23:47 +00:00
|
|
|
base = clean(base)
|
|
|
|
|
|
|
|
if err := s.Add(&Dir{}, base); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := s.children[base]; !ok {
|
2021-11-16 12:13:58 +00:00
|
|
|
s.children[base] = make(map[string]File)
|
2020-09-27 19:23:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if filename != "" {
|
|
|
|
s.children[base][filename] = f
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) Children(path string) map[string]File {
|
|
|
|
path = clean(path)
|
|
|
|
|
|
|
|
out, err := s.getDirFromFs(path)
|
|
|
|
if err == nil {
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2021-11-16 12:13:58 +00:00
|
|
|
l := make(map[string]File)
|
2020-09-27 19:23:47 +00:00
|
|
|
for n, f := range s.children[path] {
|
|
|
|
l[n] = f
|
|
|
|
}
|
|
|
|
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) Get(path string) (File, error) {
|
|
|
|
path = clean(path)
|
|
|
|
if !s.Has(path) {
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
file, ok := s.files[path]
|
|
|
|
if ok {
|
|
|
|
return file, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.getFileFromFs(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) getFileFromFs(p string) (File, error) {
|
|
|
|
for fsp, fs := range s.filesystems {
|
|
|
|
if strings.HasPrefix(p, fsp) {
|
2020-11-01 11:23:39 +00:00
|
|
|
return fs.Open(separator + strings.TrimPrefix(p, fsp))
|
2020-09-27 19:23:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) getDirFromFs(p string) (map[string]File, error) {
|
|
|
|
for fsp, fs := range s.filesystems {
|
|
|
|
if strings.HasPrefix(p, fsp) {
|
|
|
|
path := strings.TrimPrefix(p, fsp)
|
|
|
|
return fs.ReadDir(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
2020-11-01 11:23:39 +00:00
|
|
|
func clean(p string) string {
|
|
|
|
return path.Clean(separator + strings.ReplaceAll(p, "\\", "/"))
|
2020-09-27 19:23:47 +00:00
|
|
|
}
|