vfs/vfs.go

340 lines
5.8 KiB
Go

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
}