tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/mountfs/mountfs.go (about) 1 package mountfs 2 3 import ( 4 "io/fs" 5 "path/filepath" 6 "strings" 7 8 "tractor.dev/toolkit-go/engine/fs/fsutil" 9 ) 10 11 // mount.FS assumes the mount takes over at its mount root, ie you cant access 12 // the base fsys. Using Union() modifier then only does unions on shared 13 // paths. 14 func New(fsys fs.FS, root string, mount fs.FS, opts ...Modifier) *FS { 15 if root == "" { 16 root = "." 17 } 18 for _, opt := range opts { 19 mount = opt(fsys, root, mount) 20 } 21 return &FS{FS: fsys, mount: mount, root: root} 22 } 23 24 type mountDir struct { 25 fs.File 26 path string 27 fsys fs.FS 28 dirInfo fs.FileInfo 29 dirName string 30 } 31 32 func (f *mountDir) ReadDir(c int) (ofi []fs.DirEntry, err error) { 33 d, err := fs.ReadDir(f.fsys, f.path) 34 if err != nil { 35 return d, err 36 } 37 d = append(d, f) 38 return d, nil 39 } 40 41 func (f *mountDir) Name() string { 42 return f.dirName 43 } 44 func (f *mountDir) IsDir() bool { 45 return true 46 } 47 func (f *mountDir) Type() fs.FileMode { 48 return f.dirInfo.Mode().Type() 49 } 50 func (f *mountDir) Info() (fs.FileInfo, error) { 51 return f.dirInfo, nil 52 } 53 54 type FS struct { 55 fs.FS 56 57 mount fs.FS 58 root string 59 } 60 61 func (m *FS) Open(name string) (fs.File, error) { 62 if m.root == "." { 63 return m.mount.Open(name) 64 } 65 if name == m.root { 66 return m.mount.Open(".") 67 } 68 if name == filepath.Dir(m.root) { 69 // TODO: make this work when root is several dirs deep 70 fi, err := fs.Stat(m.mount, ".") 71 if err != nil { 72 return nil, err 73 } 74 f, err := m.FS.Open(name) 75 return &mountDir{File: f, path: name, fsys: m.FS, dirInfo: fi, dirName: filepath.Base(m.root)}, err 76 } 77 if strings.HasPrefix(name, m.root+"/") { 78 return m.mount.Open(strings.TrimPrefix(name, m.root+"/")) 79 } 80 return m.FS.Open(name) 81 } 82 83 func (m *FS) OpenFile(name string, flag int, perm fs.FileMode) (fs.File, error) { 84 if m.root == "." { 85 return fsutil.OpenFile(m.mount, name, flag, perm) 86 } 87 if name == m.root { 88 return fsutil.OpenFile(m.mount, ".", flag, perm) 89 } 90 if strings.HasPrefix(name, m.root+"/") { 91 return fsutil.OpenFile(m.mount, strings.TrimPrefix(name, m.root+"/"), flag, perm) 92 } 93 return fsutil.OpenFile(m.FS, name, flag, perm) 94 } 95 96 func (m *FS) Stat(name string) (fi fs.FileInfo, err error) { 97 if m.root == "." { 98 return fs.Stat(m.mount, name) 99 } 100 if name == m.root { 101 return fs.Stat(m.mount, ".") 102 } 103 if strings.HasPrefix(name, m.root+"/") { 104 return fs.Stat(m.mount, strings.TrimPrefix(name, m.root+"/")) 105 } 106 return fs.Stat(m.FS, name) 107 } 108 109 // func (m *FS) Watch(name string, cfg *watchfs.Config) (*watchfs.Watch, error) { 110 // if m.root == "." { 111 // return Watch(m.mount, name, cfg) 112 // } 113 // if name == m.root { 114 // return Watch(m.mount, ".", cfg) 115 // } 116 // if strings.HasPrefix(name, m.root+"/") { 117 // return Watch(m.mount, strings.TrimPrefix(name, m.root+"/"), cfg) 118 // } 119 // return Watch(m.FS, name, cfg) 120 // }