refactor: flatten repository structure to root and archive research notes
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
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) CreateExclusive(p string) (*os.File, error) {
|
||||
r, rel, ok := m.find(p)
|
||||
if !ok {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return r.CreateExclusive(rel)
|
||||
}
|
||||
func (m Multi) Remove(p string) error {
|
||||
r, rel, ok := m.find(p)
|
||||
if !ok || rel == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return r.Remove(rel)
|
||||
}
|
||||
|
||||
func (m Multi) Trash(p string) error {
|
||||
r, rel, ok := m.find(p)
|
||||
if !ok || rel == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return r.Trash(rel)
|
||||
}
|
||||
|
||||
func (m Multi) MoveToTrash(p, trashRoot string) error {
|
||||
r, rel, ok := m.find(p)
|
||||
if !ok || rel == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
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) Copy(a, b string) error {
|
||||
ra, aa, oka := m.find(a)
|
||||
rb, bb, okb := m.find(b)
|
||||
if !oka || !okb || aa == "" || bb == "" || ra.Root != rb.Root {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return ra.Copy(aa, bb)
|
||||
}
|
||||
func (m Multi) Rename(a, b string) error {
|
||||
ra, aa, oka := m.find(a)
|
||||
rb, bb, okb := m.find(b)
|
||||
if !oka || !okb || aa == "" || bb == "" || 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 }
|
||||
Reference in New Issue
Block a user