package vfs import ( "bytes" "fmt" "io" "io/fs" "os" "path" "strings" ) type FS struct { directory *directory } func NewVFS() *FS { return &FS{ directory: &directory{ children: make(map[string]interface{}), }, } } func (f *FS) Open(path string) (fs.File, error) { if !fs.ValidPath(path) { return nil, &fs.PathError{ Op: "Open", Path: path, Err: fs.ErrInvalid, } } if path == "." { path = "" } child, err := f.find(path) if err != nil { return nil, err } switch c := child.(type) { case *File: return &File{ name: c.name, mode: c.mode, data: bytes.NewBuffer(c.data.Bytes()), open: true, }, nil case *directory: return &directoryHandle{ dir: c, }, nil case *MockFile: return &MockFile{ name: c.name, mode: c.mode, modified: c.modified, open: true, size: c.size, }, nil } return nil, fmt.Errorf("%s is unknown file type %v", path, fs.ErrInvalid) } func (f *FS) find(path string) (interface{}, error) { if path == "" { return f.directory, nil } current := f.directory split := strings.Split(path, "/") var target interface{} var err error err = nil for i, subpath := range split { target, err = func() (interface{}, error) { current.mutex.Lock() defer current.mutex.Unlock() child := current.children[subpath] if child == nil { return nil, fmt.Errorf("%s is not a directory", subpath) } if _, ok := child.(*File); ok { if i == len(split)-1 { return child, nil } return nil, fmt.Errorf("%s does not exist", path) } if _, ok := child.(*MockFile); ok { if i == len(split)-1 { return child, nil } return nil, fmt.Errorf("%s does not exist", path) } if subdir, ok := child.(*directory); !ok { return nil, fmt.Errorf("directory %s does not exist", path) } else { current = subdir } return child, nil }() } return target, err } func (f *FS) findDirectory(path string) (*directory, error) { if path == "" { return f.directory, nil } current := f.directory split := strings.Split(path, "/") for _, subpath := range split { err := func() error { current.mutex.Lock() defer current.mutex.Unlock() child := current.children[subpath] if child == nil { return fmt.Errorf("%s is not a directory", subpath) } if subdir, ok := child.(*directory); !ok { return fmt.Errorf("directory %s does not exist", path) } else { current = subdir } return nil }() if err != nil { return nil, err } } return current, nil } func (f *FS) create(createPath string) (*File, error) { if !fs.ValidPath(createPath) { return nil, &fs.PathError{ Op: "create", Path: createPath, Err: fs.ErrInvalid, } } if createPath == "." { createPath = "" } dirName, fileName := path.Split(createPath) dirName = strings.TrimSuffix(dirName, "/") dir, err := f.findDirectory(dirName) if err != nil { return nil, err } dir.mutex.Lock() defer dir.mutex.Unlock() checkExist := dir.children[fileName] if checkExist != nil { if _, ok := checkExist.(*File); !ok { return nil, fmt.Errorf("%s is a directory that already exists", createPath) } } file := &File{ name: fileName, mode: 0666, data: &bytes.Buffer{}, } dir.children[fileName] = file return file, nil } func (f *FS) MkdirAll(path string, mode os.FileMode) error { if !fs.ValidPath(path) { return &fs.PathError{ Op: "MkdirAll", Path: path, Err: fs.ErrInvalid, } } if path == "." { return nil } split := strings.Split(path, "/") next := f.directory for _, subpath := range split { current := next current.mutex.Lock() child := current.children[subpath] if child == nil { newDir := &directory{ name: subpath, mode: mode, children: make(map[string]interface{}), } current.children[subpath] = newDir next = newDir } else { if childDir, ok := child.(*directory); !ok { current.mutex.Unlock() return fmt.Errorf("%s is not a directory", subpath) } else { next = childDir } } current.mutex.Unlock() } return nil } func (f *FS) CreateMockFile(fpath string, size int64, mode os.FileMode) error { if !fs.ValidPath(fpath) { return &fs.PathError{ Op: "WriteFile", Path: fpath, Err: fs.ErrInvalid, } } if fpath == "." { fpath = "" } err := func() error { dirName, fileName := path.Split(fpath) dirName = strings.TrimSuffix(dirName, "/") dir, err := f.findDirectory(dirName) if err != nil { return err } dir.mutex.Lock() defer dir.mutex.Unlock() checkExist := dir.children[fileName] if checkExist != nil { if _, ok := checkExist.(*File); !ok { return fmt.Errorf("%s is a directory that already exists", fpath) } } file := &MockFile{ name: fileName, mode: 0666, size: size, } dir.children[fileName] = file return nil }() if err != nil { return err } return nil } func (f *FS) WriteFile(path string, data []byte, mode os.FileMode) error { if !fs.ValidPath(path) { return &fs.PathError{ Op: "WriteFile", Path: path, Err: fs.ErrInvalid, } } if path == "." { path = "" } file, err := f.create(path) if err != nil { return err } file.data = bytes.NewBuffer(data) file.mode = mode return nil } func (f *FS) LoadDirectory(fsPath, sourcePath string) error { err := f.MkdirAll(fsPath, 0777) if err != nil { return err } files, err := os.ReadDir(sourcePath) if err != nil { return err } for _, file := range files { if !file.IsDir() { fp, err := os.Open(path.Join(sourcePath, file.Name())) if err != nil { return err } data, err := io.ReadAll(fp) if err != nil { return err } err = f.WriteFile(path.Join(fsPath, file.Name()), data, 0666) if err != nil { return err } err = fp.Close() if err != nil { return err } } } return nil }