mirror of
https://github.com/jimeh/go-golden.git
synced 2026-02-19 11:16:47 +00:00
wip: some more drastic refactoring
This commit is contained in:
275
test/testfs/testfs.go
Normal file
275
test/testfs/testfs.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package testfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
isDir bool
|
||||
}
|
||||
|
||||
type FS struct {
|
||||
Pwd string
|
||||
Nodes map[string]*Node
|
||||
}
|
||||
|
||||
func New() *FS {
|
||||
return &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o700, isDir: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) MkdirAll(name string, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
dirs := []string{name}
|
||||
for d := path.Dir(name); d != "/"; d = path.Dir(d) {
|
||||
dirs = append(dirs, d)
|
||||
}
|
||||
dirs = append(dirs, "/")
|
||||
|
||||
for i := len(dirs) - 1; i >= 0; i-- {
|
||||
dir := dirs[i]
|
||||
parent := path.Dir(dir)
|
||||
|
||||
if info, ok := fs.Nodes[dir]; ok {
|
||||
if !info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
parentInfo, ok := fs.Nodes[parent]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if !parentInfo.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions, and direct
|
||||
// parent also has write permission.
|
||||
if parentInfo.perm&0o100 == 0 || i == 1 && parentInfo.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[dir] = &Node{perm: perm, isDir: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) ReadFile(name string) ([]byte, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o400 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
return info.data, nil
|
||||
}
|
||||
|
||||
func (fs *FS) WriteFile(name string, data []byte, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if ok {
|
||||
if info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return error if file exists and has no write permission, or if the file
|
||||
// does not exist and the direct parent has no write permission.
|
||||
if ok && info.perm&0o200 == 0 || !ok && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[name] = &Node{data: data, perm: perm}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Remove(name string) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parent != nil && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
for p := range fs.Nodes {
|
||||
if strings.HasPrefix(p, name) && p != name {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("directory not empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(fs.Nodes, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Exists(name string) bool {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, ok := fs.Nodes[name]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs *FS) FileMode(name string) (os.FileMode, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
if info, ok := fs.Nodes[name]; ok {
|
||||
return info.perm, nil
|
||||
}
|
||||
|
||||
return 0, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) checkParents(absPath string, noExistError bool) (*Node, error) {
|
||||
var parents []string
|
||||
for d := path.Dir(absPath); d != "/"; d = path.Dir(d) {
|
||||
parents = append(parents, d)
|
||||
}
|
||||
parents = append(parents, "/")
|
||||
var directParent *Node
|
||||
|
||||
for i := 0; i < len(parents); i++ {
|
||||
dir := parents[i]
|
||||
info, ok := fs.Nodes[dir]
|
||||
if !ok && noExistError {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info != nil && !info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions.
|
||||
if info != nil && info.perm&0o100 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
directParent = info
|
||||
}
|
||||
}
|
||||
|
||||
return directParent, nil
|
||||
}
|
||||
Reference in New Issue
Block a user