package storage import ( "os" "path/filepath" "strconv" "strings" "time" ) type Multi struct { Roots []Local Names []string } func NewMulti(paths []string) Multi { m := Multi{} for _, p := range paths { if strings.TrimSpace(p) == "" { continue } abs, _ := filepath.Abs(p) name := filepath.Base(filepath.Clean(abs)) if name == "." || name == string(filepath.Separator) || name == "" { name = "root" } base := name for i := 2; containsString(m.Names, name); i++ { name = base + "-" + strconv.Itoa(i) } m.Roots = append(m.Roots, Local{Root: abs}) m.Names = append(m.Names, name) } return m } func containsString(items []string, want string) bool { for _, item := range items { if item == want { return true } } return false } func (m Multi) isVirtualRoot(p string) bool { return strings.Trim(p, "/") == "" } func (m Multi) FindRoot(p string) (string, string, bool) { p = strings.Trim(strings.ReplaceAll(p, "\\", "/"), "/") if p == "" { return "", "", false } parts := strings.SplitN(p, "/", 2) for _, n := range m.Names { if parts[0] != n { continue } rel := "" if len(parts) == 2 { rel = parts[1] } return n, rel, true } return "", "", false } func (m Multi) find(p string) (Local, string, bool) { p = strings.Trim(strings.ReplaceAll(p, "\\", "/"), "/") if p == "" { return Local{}, "", false } parts := strings.SplitN(p, "/", 2) for i, n := range m.Names { if parts[0] != n { continue } rel := "" if len(parts) == 2 { rel = parts[1] } return m.Roots[i], rel, true } return Local{}, "", false } func (m Multi) Stat(p string) (os.FileInfo, error) { if m.isVirtualRoot(p) { return virtualDirInfo{name: "."}, nil } r, rel, ok := m.find(p) if !ok { return nil, os.ErrNotExist } return r.Stat(rel) } func (m Multi) Open(p string) (*os.File, error) { r, rel, ok := m.find(p) if !ok { return nil, os.ErrNotExist } return r.Open(rel) } func (m Multi) Create(p string) (*os.File, error) { r, rel, ok := m.find(p) if !ok { return nil, os.ErrNotExist } return r.Create(rel) } func (m Multi) Remove(p string) error { r, rel, ok := m.find(p) if !ok { return os.ErrNotExist } return r.Remove(rel) } func (m Multi) Trash(p string) error { r, rel, ok := m.find(p) if !ok { return os.ErrNotExist } return r.Trash(rel) } func (m Multi) MoveToTrash(p, trashRoot string) error { r, rel, ok := m.find(p) if !ok { return os.ErrNotExist } return r.MoveToTrash(rel, filepath.Join(trashRoot, m.RootName(r))) } func (m Multi) RootName(r Local) string { for i := range m.Roots { if m.Roots[i].Root == r.Root { return m.Names[i] } } return "root" } func (m Multi) Rename(a, b string) error { ra, aa, oka := m.find(a) rb, bb, okb := m.find(b) if !oka || !okb || ra.Root != rb.Root { return os.ErrInvalid } return ra.Rename(aa, bb) } func (m Multi) MkdirAll(p string) error { r, rel, ok := m.find(p) if !ok { return os.ErrNotExist } return r.MkdirAll(rel) } func (m Multi) List(p string) ([]os.FileInfo, error) { p = strings.Trim(strings.ReplaceAll(p, "\\", "/"), "/") if p == "" { out := make([]os.FileInfo, 0, len(m.Names)) for _, n := range m.Names { out = append(out, virtualDirInfo{name: n}) } return out, nil } r, rel, ok := m.find(p) if !ok { return nil, os.ErrNotExist } return r.List(rel) } type virtualDirInfo struct{ name string } func (v virtualDirInfo) Name() string { return v.name } func (v virtualDirInfo) Size() int64 { return 0 } func (v virtualDirInfo) Mode() os.FileMode { return os.ModeDir | 0555 } func (v virtualDirInfo) ModTime() (t time.Time) { return time.Time{} } func (v virtualDirInfo) IsDir() bool { return true } func (v virtualDirInfo) Sys() any { return nil }