package vfs import ( "fmt" "strings" "sync" ) type ResolveFS struct { osDir string osFS *OsFS resolver *resolver } func NewResolveFS(osDir string, factories map[string]FsFactory) *ResolveFS { return &ResolveFS{ osDir: osDir, osFS: NewOsFs(osDir), resolver: newResolver(factories), } } // Open implements Filesystem. func (r *ResolveFS) Open(filename string) (File, error) { fsPath, nestedFs, nestedFsPath, err := r.resolver.resolvePath(filename, r.osFS.Open) if err != nil { return nil, err } if nestedFs != nil { return nestedFs.Open(nestedFsPath) } return r.osFS.Open(fsPath) } // ReadDir implements Filesystem. func (r *ResolveFS) ReadDir(dir string) (map[string]File, error) { fsPath, nestedFs, nestedFsPath, err := r.resolver.resolvePath(dir, r.osFS.Open) if err != nil { return nil, err } if nestedFs != nil { return nestedFs.ReadDir(nestedFsPath) } return r.osFS.ReadDir(fsPath) } var _ Filesystem = &ResolveFS{} type FsFactory func(f File) (Filesystem, error) const Separator = "/" func newResolver(factories map[string]FsFactory) *resolver { return &resolver{ factories: factories, fsmap: map[string]Filesystem{}, } } type resolver struct { m sync.Mutex factories map[string]FsFactory fsmap map[string]Filesystem // filesystem cache // TODO: add fsmap clean } type openFile func(path string) (File, error) // open requeue raw open, without resolver call func (r *resolver) resolvePath(name string, rawOpen openFile) (fsPath string, nestedFs Filesystem, nestedFsPath string, err error) { name = strings.TrimPrefix(name, Separator) parts := strings.Split(name, Separator) nestOn := -1 var nestFactory FsFactory PARTS_LOOP: for i, part := range parts { for ext, factory := range r.factories { if strings.HasSuffix(part, ext) { nestOn = i + 1 nestFactory = factory break PARTS_LOOP } } } if nestOn == -1 { return name, nil, "", nil } fsPath = Clean(strings.Join(parts[:nestOn], Separator)) nestedFsPath = Clean(strings.Join(parts[nestOn:], Separator)) // we dont need lock until now // it must be before fsmap read to exclude race condition: // read -> write // read -> write r.m.Lock() defer r.m.Unlock() if nestedFs, ok := r.fsmap[fsPath]; ok { return fsPath, nestedFs, nestedFsPath, nil } else { fsFile, err := rawOpen(fsPath) if err != nil { return "", nil, "", fmt.Errorf("error opening filesystem file: %s with error: %w", fsPath, err) } nestedFs, err := nestFactory(fsFile) if err != nil { return "", nil, "", fmt.Errorf("error creating filesystem from file: %s with error: %w", fsPath, err) } r.fsmap[fsPath] = nestedFs return fsPath, nestedFs, nestedFsPath, nil } } // func (r *resolver) resolveFile(name string, fs Filesystem) (File, error) { // fsPath, nestedFs, nestedFsPath, err := r.resolvePath(name, fs) // if err != nil { // return nil, err // } // if nestedFs == nil { // return fs.Open(fsPath) // } // return nestedFs.Open(nestedFsPath) // } // func (r *resolver) resolveDir(name string, fs Filesystem) (map[string]File, error) { // fsPath, nestedFs, nestedFsPath, err := r.resolvePath(name, fs) // if err != nil { // return nil, err // } // if nestedFs == nil { // return fs.ReadDir(fsPath) // } // return nestedFs.ReadDir(nestedFsPath) // }