package vfs import ( "fmt" "io/fs" "path" ) // Map maps mapping over every name lookup in the resulting [FS]. // // mapping's name is of style "path" (as with [io/fs.FS]) and cleaned. // mappings returned string is "path"-cleaned // // Both mapping and fs must be non-nil. func Map(mapping func(name string) (string, error), fs FS) *Mapped { return &Mapped{ mapping: func(name string) (string, error) { name, err := mapping(path.Join(".", name)) if err != nil { return "", err } return path.Join(".", name), nil }, fs: fs, } } type Mapped struct { mapping func(name string) (string, error) fs FS } func (mapped *Mapped) Lstat(name string) (fs.FileInfo, error) { name0 := name name, err := mapped.mapping(name) if err != nil { return nil, err } info, err := mapped.fs.Lstat(name) if err != nil { return nil, wrap(err, name0, name) } return info, nil } func (mapped *Mapped) Chmod(name string, perm fs.FileMode) error { name0 := name name, err := mapped.mapping(name) if err != nil { return err } return wrap(mapped.fs.Chmod(name, perm), name0, name) } func (mapped *Mapped) MkdirAll(name string, perm fs.FileMode) error { name0 := name name, err := mapped.mapping(name) if err != nil { return err } return wrap(mapped.fs.MkdirAll(name, perm), name0, name) } func (mapped *Mapped) Remove(name string) error { name0 := name name, err := mapped.mapping(name) if err != nil { return err } return wrap(mapped.fs.Remove(name), name0, name) } func (mapped *Mapped) ReadDir(name string) ([]fs.DirEntry, error) { name0 := name name, err := mapped.mapping(name) if err != nil { return nil, wrap(err, name0, name) } return mapped.fs.ReadDir(name) } func (mapped *Mapped) Rename(oldname, newname string) error { oldname0 := oldname oldname, err := mapped.mapping(oldname) if err != nil { return err } newname0 := newname newname, err = mapped.mapping(newname) if err != nil { return err } return wrap(mapped.fs.Rename(oldname, newname), oldname0, oldname, newname0, newname) } func (mapped *Mapped) OpenFile(name string, flag int, perm fs.FileMode) (ReadWriteSyncCloser, error) { name0 := name name, err := mapped.mapping(name) if err != nil { return nil, err } rwsc, err := mapped.fs.OpenFile(name, flag, perm) if err != nil { return nil, wrap(err, name0, name) } return rwsc, nil } func wrap(err error, name0names ...string) error { switch { default: panic("unreachable") case len(name0names) == 2: return fmt.Errorf("mapped {%q->%q}: %w", name0names[0], name0names[1], err) case len(name0names) == 4: return fmt.Errorf("mapped {%q->%q, %q->%q}: %w", name0names[0], name0names[1], name0names[2], name0names[3], err) } }