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  // }