github.com/kyleu/dbaudit@v0.0.2-0.20240321155047-ff2f2c940496/app/lib/filesystem/filesystem.go (about)

     1  // Package filesystem - Content managed by Project Forge, see [projectforge.md] for details.
     2  package filesystem
     3  
     4  import (
     5  	"fmt"
     6  	"path/filepath"
     7  	"runtime"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/spf13/afero"
    12  )
    13  
    14  var memFS, osFS afero.Fs
    15  
    16  func MemFS() afero.Fs {
    17  	if memFS == nil {
    18  		memFS = afero.NewMemMapFs()
    19  	}
    20  	return memFS
    21  }
    22  
    23  func OSFS() afero.Fs {
    24  	if osFS == nil {
    25  		osFS = afero.NewOsFs()
    26  	}
    27  	return osFS
    28  }
    29  
    30  type FileSystem struct {
    31  	Mode     string `json:"mode,omitempty"`
    32  	ReadOnly bool   `json:"readOnly,omitempty"`
    33  	root     string
    34  	f        afero.Fs
    35  }
    36  
    37  var _ FileLoader = (*FileSystem)(nil)
    38  
    39  func NewFileSystem(root string, readonly bool, mode string) (*FileSystem, error) {
    40  	root = strings.TrimPrefix(root, "./")
    41  	var f afero.Fs
    42  	switch mode {
    43  	case "memory":
    44  		f = MemFS()
    45  	case "file":
    46  		f = OSFS()
    47  	case "":
    48  		switch runtime.GOOS {
    49  		case "js":
    50  			f = MemFS()
    51  		default:
    52  			f = OSFS()
    53  		}
    54  	default:
    55  		return nil, errors.Errorf("invalid filesystem mode [%s]", mode)
    56  	}
    57  	if readonly {
    58  		f = afero.NewReadOnlyFs(f)
    59  	}
    60  	return &FileSystem{root: root, f: f}, nil
    61  }
    62  
    63  func (f *FileSystem) getPath(ss ...string) string {
    64  	s := filepath.Join(ss...)
    65  	if strings.HasPrefix(s, f.root) {
    66  		return s
    67  	}
    68  	return filepath.Join(f.root, s)
    69  }
    70  
    71  func (f *FileSystem) Root() string {
    72  	return f.root
    73  }
    74  
    75  func (f *FileSystem) Clone() FileLoader {
    76  	ret, _ := NewFileSystem(f.root, f.ReadOnly, f.Mode)
    77  	return ret
    78  }
    79  
    80  func (f *FileSystem) Stat(path string) (*FileInfo, error) {
    81  	p := f.getPath(path)
    82  	s, err := f.f.Stat(p)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return FileInfoFromFS(s), nil
    87  }
    88  
    89  func (f *FileSystem) SetMode(path string, mode FileMode) error {
    90  	p := f.getPath(path)
    91  	return f.f.Chmod(p, mode.ToFS())
    92  }
    93  
    94  func (f *FileSystem) Exists(path string) bool {
    95  	x, _ := f.Stat(path)
    96  	return x != nil
    97  }
    98  
    99  func (f *FileSystem) IsDir(path string) bool {
   100  	s, err := f.Stat(path)
   101  	if s == nil || err != nil {
   102  		return false
   103  	}
   104  	return s.IsDir
   105  }
   106  
   107  func (f *FileSystem) CreateDirectory(path string) error {
   108  	p := f.getPath(path)
   109  	if err := f.f.MkdirAll(p, DirectoryMode.ToFS()); err != nil {
   110  		return errors.Wrapf(err, "unable to create data directory [%s]", p)
   111  	}
   112  	return nil
   113  }
   114  
   115  func (f *FileSystem) String() string {
   116  	return fmt.Sprintf("fs://%s", f.root)
   117  }