45f10e2f81
- Using cgofuse to be compatible with multiple OSes - Refactor to make possible better testing - Add a bunch of tests - Add code coverage Signed-off-by: Antonio Navarro Perez <antnavper@gmail.com>
151 lines
2.6 KiB
Go
151 lines
2.6 KiB
Go
package fs
|
|
|
|
import (
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
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{
|
|
files: make(map[string]File, 0),
|
|
children: make(map[string]map[string]File, 0),
|
|
filesystems: make(map[string]Filesystem, 0),
|
|
factories: factories,
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
s.createParent(p, f)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *storage) createParent(path string, f File) error {
|
|
base, filename := filepath.Split(path)
|
|
base = clean(base)
|
|
|
|
if err := s.Add(&Dir{}, base); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, ok := s.children[base]; !ok {
|
|
s.children[base] = make(map[string]File, 0)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
l := make(map[string]File, 0)
|
|
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) {
|
|
return fs.Open(string(os.PathSeparator) + strings.TrimPrefix(p, fsp))
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func clean(path string) string {
|
|
return filepath.Clean(string(os.PathSeparator) + filepath.FromSlash(path))
|
|
}
|