refactor: audit fsedsl exports — remove dead code, unexport internals
Dead code removed: - CreateFile option + FsNodeDecl.Create field (never set in production) - Alias option function (Binding.Aliases field retained — used by fs/) - FileTree interface (unused anywhere) - Validation for Create-on-non-dir (no longer possible) Unexported (internal-only plumbing): - NewTree → newTree, TreeOption → treeOption - WithLister/Creator/Deleter/Renamer/Opener/Stat/Readdir → with* - FileConfig → fileConfig - SyntheticEntry → syntheticEntry - FileEntry/DirEntry → fileEntry/dirEntry Retained (used by fs/): - Tree, BuildTree, FsNodeDecl, File, SyntheticFileInfo - All active NodeOptions (Read/Write/Stream/BlockOnce/Request/ RemoveNode/StatOverride/UID/GID/Bind/Doc) - Perm* constants, Binding, Each, DirNode, FileNode - GenerateHelp, NodeOption type
This commit is contained in:
parent
199acabd61
commit
f5efd90fbe
|
|
@ -21,7 +21,6 @@ func validate[Ctx any](d *FsNodeDecl[Ctx], path string) {
|
|||
hasChildren := len(d.Children) > 0
|
||||
hasBindings := d.Bindings != nil
|
||||
hasFileHandlers := d.Read != nil || d.Write != nil || d.BlockOnce != nil || d.Stream != nil || d.Request != nil
|
||||
hasCreate := d.Create != nil
|
||||
hasRemove := d.Remove != nil
|
||||
|
||||
// A node is a directory if it has children.
|
||||
|
|
@ -55,10 +54,6 @@ func validate[Ctx any](d *FsNodeDecl[Ctx], path string) {
|
|||
panic(fmt.Sprintf("BuildTree: %q: at most one of BlockOnce, Stream, Request may be set", fullPath))
|
||||
}
|
||||
|
||||
// Create only valid on directory nodes.
|
||||
if hasCreate && !isDir {
|
||||
panic(fmt.Sprintf("BuildTree: %q: Create on non-directory node", fullPath))
|
||||
}
|
||||
// Remove is valid on directories and file templates (Bindings with file handlers).
|
||||
isFileTemplate := hasBindings && !isDir && hasFileHandlers
|
||||
if hasRemove && !isDir && !isFileTemplate {
|
||||
|
|
@ -99,29 +94,26 @@ func build[Ctx any](d *FsNodeDecl[Ctx], parentUid, parentGid string, parentCtx C
|
|||
mode = 0755
|
||||
}
|
||||
|
||||
t := NewTree(nil, mode,
|
||||
WithLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
t := newTree(nil, mode,
|
||||
withLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return listDir(d, uid, gid, parentCtx)
|
||||
}),
|
||||
WithReaddir(func(_ []string, name string) ([]os.DirEntry, error) {
|
||||
withReaddir(func(_ []string, name string) ([]os.DirEntry, error) {
|
||||
return readdirDir(d, uid, gid, name, parentCtx)
|
||||
}),
|
||||
WithStat(func(_ []string, name string) (os.FileInfo, error) {
|
||||
withStat(func(_ []string, name string) (os.FileInfo, error) {
|
||||
return statDir(d, uid, gid, name, parentCtx)
|
||||
}),
|
||||
WithOpener(func(_ []string, name string) (File, error) {
|
||||
withOpener(func(_ []string, name string) (File, error) {
|
||||
return openDir(d, uid, gid, name, parentCtx)
|
||||
}),
|
||||
WithDeleter(func(_ []string, name string) error {
|
||||
withDeleter(func(_ []string, name string) error {
|
||||
return deleteDir(d, name, parentCtx)
|
||||
}),
|
||||
WithCreator(func(_ []string, name string, _ os.FileMode) error {
|
||||
if d.Create != nil {
|
||||
return d.Create(parentCtx, name)
|
||||
}
|
||||
withCreator(func(_ []string, name string, _ os.FileMode) error {
|
||||
return fmt.Errorf("create not supported")
|
||||
}),
|
||||
WithRenamer(func(_ []string, old, new string) error {
|
||||
withRenamer(func(_ []string, old, new string) error {
|
||||
return renameDir(d, old, new, parentCtx)
|
||||
}),
|
||||
)
|
||||
|
|
@ -132,11 +124,11 @@ func build[Ctx any](d *FsNodeDecl[Ctx], parentUid, parentGid string, parentCtx C
|
|||
if mode == 0 {
|
||||
mode = 0444
|
||||
}
|
||||
return NewTree(nil, 0,
|
||||
WithLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return newTree(nil, 0,
|
||||
withLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return nil, fmt.Errorf("%s: is a file", d.Name)
|
||||
}),
|
||||
WithOpener(func(_ []string, name string) (File, error) {
|
||||
withOpener(func(_ []string, name string) (File, error) {
|
||||
return openLeaf(d, uid, gid, mode, parentCtx, name)
|
||||
}),
|
||||
)
|
||||
|
|
@ -165,9 +157,9 @@ func listDir[Ctx any](d *FsNodeDecl[Ctx], uid, gid string, ctx Ctx) ([]os.DirEnt
|
|||
m = 0755
|
||||
}
|
||||
if isFileTemplate {
|
||||
entries = append(entries, FileEntry(b.Name, m))
|
||||
entries = append(entries, fileEntry(b.Name, m))
|
||||
} else {
|
||||
entries = append(entries, DirEntry(b.Name, m))
|
||||
entries = append(entries, dirEntry(b.Name, m))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,9 +173,9 @@ func listDir[Ctx any](d *FsNodeDecl[Ctx], uid, gid string, ctx Ctx) ([]os.DirEnt
|
|||
hasFileHandlers := c.Read != nil || c.Write != nil || c.BlockOnce != nil || c.Stream != nil || c.Request != nil
|
||||
isDir := len(c.Children) > 0 || (c.Bindings != nil && !hasFileHandlers)
|
||||
if isDir {
|
||||
entries = append(entries, DirEntry(c.Name, m))
|
||||
entries = append(entries, dirEntry(c.Name, m))
|
||||
} else {
|
||||
entries = append(entries, FileEntry(c.Name, m))
|
||||
entries = append(entries, fileEntry(c.Name, m))
|
||||
}
|
||||
}
|
||||
return entries, nil
|
||||
|
|
@ -323,7 +315,7 @@ func openLeaf[Ctx any](d *FsNodeDecl[Ctx], uid, gid string, mode os.FileMode, ct
|
|||
blockFn = notBlocking
|
||||
}
|
||||
|
||||
return &FileConfig{
|
||||
return &fileConfig{
|
||||
StatFn: func() (os.FileInfo, error) {
|
||||
if d.Stat != nil {
|
||||
return d.Stat(), nil
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ type FsNodeDecl[Ctx any] struct {
|
|||
// Request — rdwr: write triggers result, read returns it once.
|
||||
Request func(ctx Ctx, data []byte) ([]byte, error)
|
||||
|
||||
// Tcreate + Tremove + Trename (directory ops).
|
||||
Create func(ctx Ctx, name string) error
|
||||
// Tremove + Trename (directory ops).
|
||||
Remove func(ctx Ctx) error
|
||||
Rename func(newName string) error
|
||||
}
|
||||
|
|
@ -181,11 +180,6 @@ func Request[Ctx any](fn func(Ctx, []byte) ([]byte, error)) NodeOption[Ctx] {
|
|||
return func(d *FsNodeDecl[Ctx]) { d.Request = fn }
|
||||
}
|
||||
|
||||
// CreateFile sets the file creation handler for a directory node.
|
||||
func CreateFile[Ctx any](fn func(Ctx, string) error) NodeOption[Ctx] {
|
||||
return func(d *FsNodeDecl[Ctx]) { d.Create = fn }
|
||||
}
|
||||
|
||||
// RemoveNode sets the removal handler for a node.
|
||||
func RemoveNode[Ctx any](fn func(Ctx) error) NodeOption[Ctx] {
|
||||
return func(d *FsNodeDecl[Ctx]) { d.Remove = fn }
|
||||
|
|
@ -206,11 +200,6 @@ func GID[Ctx any](gid string) NodeOption[Ctx] {
|
|||
return func(d *FsNodeDecl[Ctx]) { d.GID = gid }
|
||||
}
|
||||
|
||||
// Alias adds alternate names for resolving this node.
|
||||
func Alias[Ctx any](names ...string) NodeOption[Ctx] {
|
||||
return func(d *FsNodeDecl[Ctx]) { d.Aliases = append(d.Aliases, names...) }
|
||||
}
|
||||
|
||||
// Bind declares how Each bindings populate the context.
|
||||
// This is advisory metadata; the actual binding is done by Binding.Applier.
|
||||
func Bind[Ctx any](targets ...BindTarget) NodeOption[Ctx] {
|
||||
|
|
|
|||
|
|
@ -51,20 +51,6 @@ func TestNodeOptions(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
// Test CreateFile option
|
||||
t.Run("CreateFile", func(t *testing.T) {
|
||||
createFn := func(ctx TestCtx, name string) error {
|
||||
return nil
|
||||
}
|
||||
spec := DirNode[TestCtx]("test",
|
||||
FileNode[TestCtx]("child", 0444),
|
||||
CreateFile[TestCtx](createFn),
|
||||
)
|
||||
if spec.Create == nil {
|
||||
t.Error("Create not set")
|
||||
}
|
||||
})
|
||||
|
||||
// Test RemoveNode option
|
||||
t.Run("RemoveNode", func(t *testing.T) {
|
||||
removeFn := func(ctx TestCtx) error {
|
||||
|
|
@ -107,16 +93,6 @@ func TestNodeOptions(t *testing.T) {
|
|||
})
|
||||
|
||||
// Test Alias option
|
||||
t.Run("Alias", func(t *testing.T) {
|
||||
spec := FileNode[TestCtx]("test", 0444, Alias[TestCtx]("alt1", "alt2"))
|
||||
if len(spec.Aliases) != 2 {
|
||||
t.Errorf("Aliases = %v, want 2 elements", spec.Aliases)
|
||||
}
|
||||
if spec.Aliases[0] != "alt1" || spec.Aliases[1] != "alt2" {
|
||||
t.Errorf("Aliases = %v, want [alt1, alt2]", spec.Aliases)
|
||||
}
|
||||
})
|
||||
|
||||
// Test Bind option
|
||||
t.Run("Bind", func(t *testing.T) {
|
||||
spec := DirNode[TestCtx]("test",
|
||||
|
|
@ -134,10 +110,10 @@ func TestNodeOptions(t *testing.T) {
|
|||
// ══════════════════════════════════════════════════════════════════
|
||||
|
||||
func TestTreeMount(t *testing.T) {
|
||||
parent := NewTree(nil, 0755)
|
||||
child := NewTree(nil, 0755,
|
||||
WithLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{FileEntry("childfile", 0444)}, nil
|
||||
parent := newTree(nil, 0755)
|
||||
child := newTree(nil, 0755,
|
||||
withLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{fileEntry("childfile", 0444)}, nil
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -166,8 +142,8 @@ func TestTreeMount(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeSplit(t *testing.T) {
|
||||
parent := NewTree(nil, 0755)
|
||||
child := NewTree(nil, 0755)
|
||||
parent := newTree(nil, 0755)
|
||||
child := newTree(nil, 0755)
|
||||
parent.Mount("sub", child)
|
||||
|
||||
// Test exact match
|
||||
|
|
@ -201,7 +177,7 @@ func TestTreeSplit(t *testing.T) {
|
|||
}
|
||||
|
||||
// Test tree without children
|
||||
empty := NewTree(nil, 0755)
|
||||
empty := newTree(nil, 0755)
|
||||
_, _, ok4 := empty.split("anything")
|
||||
if ok4 {
|
||||
t.Error("split should not find anything in empty tree")
|
||||
|
|
@ -219,11 +195,11 @@ func TestTreeIndexOf(t *testing.T) {
|
|||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
// FileConfig tests
|
||||
// fileConfig tests
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
|
||||
func TestFileConfigMethods(t *testing.T) {
|
||||
fc := &FileConfig{
|
||||
fc := &fileConfig{
|
||||
StatFn: func() (os.FileInfo, error) { return &SyntheticFileInfo{Name_: "test"}, nil },
|
||||
ReadFn: func() ([]byte, error) { return []byte("content"), nil },
|
||||
WriteFn: func(data []byte) error { return nil },
|
||||
|
|
@ -257,25 +233,25 @@ func TestFileConfigMethods(t *testing.T) {
|
|||
|
||||
func TestFileConfigModes(t *testing.T) {
|
||||
// Default: all false
|
||||
fc := &FileConfig{}
|
||||
fc := &fileConfig{}
|
||||
if fc.BlockingReadMode() || fc.StreamMode() || fc.RequestRespMode() {
|
||||
t.Error("default modes should be false")
|
||||
}
|
||||
|
||||
// BlockingReadMode
|
||||
fc2 := &FileConfig{BlockingRead_: true}
|
||||
fc2 := &fileConfig{BlockingRead_: true}
|
||||
if !fc2.BlockingReadMode() {
|
||||
t.Error("BlockingReadMode should be true")
|
||||
}
|
||||
|
||||
// StreamMode
|
||||
fc3 := &FileConfig{StreamMode_: true}
|
||||
fc3 := &fileConfig{StreamMode_: true}
|
||||
if !fc3.StreamMode() {
|
||||
t.Error("StreamMode should be true")
|
||||
}
|
||||
|
||||
// RequestRespMode
|
||||
fc4 := &FileConfig{RequestResp_: true}
|
||||
fc4 := &fileConfig{RequestResp_: true}
|
||||
if !fc4.RequestRespMode() {
|
||||
t.Error("RequestRespMode should be true")
|
||||
}
|
||||
|
|
@ -323,12 +299,12 @@ func TestSyntheticFileInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
// SyntheticEntry tests
|
||||
// syntheticEntry tests
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
|
||||
func TestSyntheticEntry(t *testing.T) {
|
||||
// File entry
|
||||
fe := &SyntheticEntry{Name_: "file", Mode_: 0644, IsDir_: false}
|
||||
fe := &syntheticEntry{name: "file", mode: 0644, isDir: false}
|
||||
if fe.Name() != "file" {
|
||||
t.Errorf("Name = %q", fe.Name())
|
||||
}
|
||||
|
|
@ -347,7 +323,7 @@ func TestSyntheticEntry(t *testing.T) {
|
|||
}
|
||||
|
||||
// Dir entry
|
||||
de := &SyntheticEntry{Name_: "dir", Mode_: 0755, IsDir_: true}
|
||||
de := &syntheticEntry{name: "dir", mode: 0755, isDir: true}
|
||||
if !de.IsDir() {
|
||||
t.Error("IsDir should be true")
|
||||
}
|
||||
|
|
@ -357,12 +333,12 @@ func TestSyntheticEntry(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFileEntryAndDirEntry(t *testing.T) {
|
||||
fe := FileEntry("myfile", 0644)
|
||||
fe := fileEntry("myfile", 0644)
|
||||
if fe.Name() != "myfile" || fe.IsDir() {
|
||||
t.Error("FileEntry wrong")
|
||||
t.Error("fileEntry wrong")
|
||||
}
|
||||
|
||||
de := DirEntry("mydir", 0755)
|
||||
de := dirEntry("mydir", 0755)
|
||||
if de.Name() != "mydir" || !de.IsDir() {
|
||||
t.Error("DirEntry wrong")
|
||||
}
|
||||
|
|
@ -730,21 +706,6 @@ func TestValidateMultipleBlockingModes(t *testing.T) {
|
|||
BuildTree(spec, TestCtx{})
|
||||
}
|
||||
|
||||
func TestValidateCreateOnNonDir(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Error("should panic for Create on non-dir")
|
||||
}
|
||||
}()
|
||||
|
||||
spec := DirNode[TestCtx]("/",
|
||||
FileNode[TestCtx]("file", 0444,
|
||||
CreateFile(func(ctx TestCtx, name string) error { return nil }),
|
||||
),
|
||||
)
|
||||
BuildTree(spec, TestCtx{})
|
||||
}
|
||||
|
||||
func TestValidateTemplateWithoutBindings(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
|
|
@ -1010,7 +971,7 @@ func TestStatDirNotFound(t *testing.T) {
|
|||
func TestCreateNotSupported(t *testing.T) {
|
||||
spec := DirNode[TestCtx]("/",
|
||||
FileNode[TestCtx]("file", 0444),
|
||||
// No CreateFile handler
|
||||
// No create handler — always returns error
|
||||
)
|
||||
|
||||
tree := BuildTree(spec, TestCtx{})
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Standard permission constants for EDSL-built trees.
|
||||
const (
|
||||
PermChildDir os.FileMode = 0555
|
||||
PermIdx os.FileMode = 0444
|
||||
PermMkdir os.FileMode = 0755
|
||||
PermMkdirPrivate os.FileMode = 0700
|
||||
)
|
||||
|
||||
// File is an open handle to a single named blob.
|
||||
type File interface {
|
||||
Stat() (os.FileInfo, error)
|
||||
|
|
@ -22,18 +30,8 @@ type File interface {
|
|||
RequestRespMode() bool
|
||||
}
|
||||
|
||||
// FileTree is a named collection of entries.
|
||||
type FileTree interface {
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
List() ([]os.DirEntry, error)
|
||||
Open(name string) (File, error)
|
||||
Create(name string) error
|
||||
Delete(name string) error
|
||||
Rename(oldName, newName string) error
|
||||
}
|
||||
|
||||
// FileConfig implements File via function pointers.
|
||||
type FileConfig struct {
|
||||
// fileConfig implements File via function pointers.
|
||||
type fileConfig struct {
|
||||
StatFn func() (os.FileInfo, error)
|
||||
ReadFn func() ([]byte, error)
|
||||
WriteFn func([]byte) error
|
||||
|
|
@ -43,15 +41,15 @@ type FileConfig struct {
|
|||
RequestResp_ bool // rdwr: write blocks on write, read returns result once
|
||||
}
|
||||
|
||||
func (e *FileConfig) Stat() (os.FileInfo, error) { return e.StatFn() }
|
||||
func (e *FileConfig) Read() ([]byte, error) { return e.ReadFn() }
|
||||
func (e *FileConfig) Write(data []byte) error { return e.WriteFn(data) }
|
||||
func (e *FileConfig) BlockingRead(ctx context.Context, base string) ([]byte, string, error) {
|
||||
func (e *fileConfig) Stat() (os.FileInfo, error) { return e.StatFn() }
|
||||
func (e *fileConfig) Read() ([]byte, error) { return e.ReadFn() }
|
||||
func (e *fileConfig) Write(data []byte) error { return e.WriteFn(data) }
|
||||
func (e *fileConfig) BlockingRead(ctx context.Context, base string) ([]byte, string, error) {
|
||||
return e.BlockingReadFn(ctx, base)
|
||||
}
|
||||
func (e *FileConfig) BlockingReadMode() bool { return e.BlockingRead_ }
|
||||
func (e *FileConfig) StreamMode() bool { return e.StreamMode_ }
|
||||
func (e *FileConfig) RequestRespMode() bool { return e.RequestResp_ }
|
||||
func (e *fileConfig) BlockingReadMode() bool { return e.BlockingRead_ }
|
||||
func (e *fileConfig) StreamMode() bool { return e.StreamMode_ }
|
||||
func (e *fileConfig) RequestRespMode() bool { return e.RequestResp_ }
|
||||
|
||||
// SyntheticFileInfo implements os.FileInfo for entries with no backing file.
|
||||
type SyntheticFileInfo struct {
|
||||
|
|
@ -71,39 +69,31 @@ func (f *SyntheticFileInfo) ModTime() time.Time { return time.Time{} }
|
|||
func (f *SyntheticFileInfo) IsDir() bool { return f.IsDir_ }
|
||||
func (f *SyntheticFileInfo) Sys() any { return nil }
|
||||
|
||||
// SyntheticEntry implements os.DirEntry for entries with no backing file.
|
||||
type SyntheticEntry struct {
|
||||
Name_ string
|
||||
Mode_ os.FileMode
|
||||
IsDir_ bool
|
||||
// syntheticEntry implements os.DirEntry for entries with no backing file.
|
||||
type syntheticEntry struct {
|
||||
name string
|
||||
mode os.FileMode
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (e *SyntheticEntry) Name() string { return e.Name_ }
|
||||
func (e *SyntheticEntry) IsDir() bool { return e.IsDir_ }
|
||||
func (e *SyntheticEntry) Type() os.FileMode {
|
||||
if e.IsDir_ {
|
||||
func (e *syntheticEntry) Name() string { return e.name }
|
||||
func (e *syntheticEntry) IsDir() bool { return e.isDir }
|
||||
func (e *syntheticEntry) Type() os.FileMode {
|
||||
if e.isDir {
|
||||
return os.ModeDir
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (e *SyntheticEntry) Info() (os.FileInfo, error) {
|
||||
return &SyntheticFileInfo{Name_: e.Name_, Mode_: e.Mode_, IsDir_: e.IsDir_}, nil
|
||||
func (e *syntheticEntry) Info() (os.FileInfo, error) {
|
||||
return &SyntheticFileInfo{Name_: e.name, Mode_: e.mode, IsDir_: e.isDir}, nil
|
||||
}
|
||||
|
||||
// FileEntry returns a synthetic file DirEntry.
|
||||
func FileEntry(name string, mode os.FileMode) os.DirEntry {
|
||||
return &SyntheticEntry{Name_: name, Mode_: mode}
|
||||
// fileEntry returns a synthetic file DirEntry.
|
||||
func fileEntry(name string, mode os.FileMode) os.DirEntry {
|
||||
return &syntheticEntry{name: name, mode: mode}
|
||||
}
|
||||
|
||||
// DirEntry returns a synthetic directory DirEntry.
|
||||
func DirEntry(name string, mode os.FileMode) os.DirEntry {
|
||||
return &SyntheticEntry{Name_: name, Mode_: mode, IsDir_: true}
|
||||
// dirEntry returns a synthetic directory DirEntry.
|
||||
func dirEntry(name string, mode os.FileMode) os.DirEntry {
|
||||
return &syntheticEntry{name: name, mode: mode, isDir: true}
|
||||
}
|
||||
|
||||
// Structural permissions — used by Tree for mounted children and synthetic files.
|
||||
const (
|
||||
PermChildDir os.FileMode = 0555
|
||||
PermIdx os.FileMode = 0444
|
||||
PermMkdir os.FileMode = 0755
|
||||
PermMkdirPrivate os.FileMode = 0700
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,47 +22,47 @@ type Tree struct {
|
|||
Data any // user-attached state
|
||||
}
|
||||
|
||||
// TreeOption configures a Tree.
|
||||
type TreeOption func(*Tree)
|
||||
// treeOption configures a Tree.
|
||||
type treeOption func(*Tree)
|
||||
|
||||
// WithLister overrides how the tree lists its entries.
|
||||
func WithLister(fn func(dirs []string) ([]os.DirEntry, error)) TreeOption {
|
||||
// withLister overrides how the tree lists its entries.
|
||||
func withLister(fn func(dirs []string) ([]os.DirEntry, error)) treeOption {
|
||||
return func(d *Tree) { d.listFn = fn }
|
||||
}
|
||||
|
||||
// WithCreator overrides file creation logic.
|
||||
func WithCreator(fn func(dirs []string, name string, perm os.FileMode) error) TreeOption {
|
||||
// withCreator overrides file creation logic.
|
||||
func withCreator(fn func(dirs []string, name string, perm os.FileMode) error) treeOption {
|
||||
return func(d *Tree) { d.createFn = fn }
|
||||
}
|
||||
|
||||
// WithDeleter overrides file deletion logic.
|
||||
func WithDeleter(fn func(dirs []string, name string) error) TreeOption {
|
||||
// withDeleter overrides file deletion logic.
|
||||
func withDeleter(fn func(dirs []string, name string) error) treeOption {
|
||||
return func(d *Tree) { d.deleteFn = fn }
|
||||
}
|
||||
|
||||
// WithRenamer overrides rename logic.
|
||||
func WithRenamer(fn func(dirs []string, old, new string) error) TreeOption {
|
||||
// withRenamer overrides rename logic.
|
||||
func withRenamer(fn func(dirs []string, old, new string) error) treeOption {
|
||||
return func(d *Tree) { d.renameFn = fn }
|
||||
}
|
||||
|
||||
// WithOpener overrides how entries are opened.
|
||||
func WithOpener(fn func(dirs []string, name string) (File, error)) TreeOption {
|
||||
// withOpener overrides how entries are opened.
|
||||
func withOpener(fn func(dirs []string, name string) (File, error)) treeOption {
|
||||
return func(d *Tree) { d.openFn = fn }
|
||||
}
|
||||
|
||||
// WithStat overrides how entries are stat'd.
|
||||
func WithStat(fn func(dirs []string, name string) (os.FileInfo, error)) TreeOption {
|
||||
// withStat overrides how entries are stat'd.
|
||||
func withStat(fn func(dirs []string, name string) (os.FileInfo, error)) treeOption {
|
||||
return func(d *Tree) { d.statFn = fn }
|
||||
}
|
||||
|
||||
// WithReaddir overrides subdirectory listing.
|
||||
func WithReaddir(fn func(dirs []string, name string) ([]os.DirEntry, error)) TreeOption {
|
||||
// withReaddir overrides subdirectory listing.
|
||||
func withReaddir(fn func(dirs []string, name string) ([]os.DirEntry, error)) treeOption {
|
||||
return func(d *Tree) { d.readdirFn = fn }
|
||||
}
|
||||
|
||||
// NewTree creates a Tree. In EDSL usage, dirs is nil and all behavior
|
||||
// newTree creates a Tree. In EDSL usage, dirs is nil and all behavior
|
||||
// is provided via options.
|
||||
func NewTree(dirs []string, perm os.FileMode, opts ...TreeOption) *Tree {
|
||||
func newTree(dirs []string, perm os.FileMode, opts ...treeOption) *Tree {
|
||||
d := &Tree{Tree: dirs, perm: perm}
|
||||
for _, o := range opts {
|
||||
o(d)
|
||||
|
|
@ -144,14 +144,14 @@ func (d *Tree) List() ([]os.DirEntry, error) {
|
|||
return nil, err
|
||||
}
|
||||
for name := range d.children {
|
||||
entries = append(entries, DirEntry(name, PermChildDir))
|
||||
entries = append(entries, dirEntry(name, PermChildDir))
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
// No list function and no backing dirs — return children only.
|
||||
var result []os.DirEntry
|
||||
for name := range d.children {
|
||||
result = append(result, DirEntry(name, PermChildDir))
|
||||
result = append(result, dirEntry(name, PermChildDir))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
func TestTreeMkdirAll(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
tree := NewTree([]string{tmpDir}, 0755)
|
||||
tree := newTree([]string{tmpDir}, 0755)
|
||||
|
||||
err := tree.MkdirAll("a/b/c")
|
||||
if err != nil {
|
||||
|
|
@ -25,7 +25,7 @@ func TestTreeMkdirAll(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeMkdirAllNoBackingDir(t *testing.T) {
|
||||
tree := NewTree(nil, 0755)
|
||||
tree := newTree(nil, 0755)
|
||||
err := tree.MkdirAll("somedir")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nil backing dir")
|
||||
|
|
@ -34,10 +34,10 @@ func TestTreeMkdirAllNoBackingDir(t *testing.T) {
|
|||
|
||||
func TestTreeWithCustomFunctions(t *testing.T) {
|
||||
listed := false
|
||||
tree := NewTree(nil, 0755,
|
||||
WithLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
tree := newTree(nil, 0755,
|
||||
withLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
listed = true
|
||||
return []os.DirEntry{FileEntry("a", 0644)}, nil
|
||||
return []os.DirEntry{fileEntry("a", 0644)}, nil
|
||||
}),
|
||||
)
|
||||
entries, err := tree.List()
|
||||
|
|
@ -53,9 +53,9 @@ func TestTreeWithCustomFunctions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeOpen(t *testing.T) {
|
||||
tree := NewTree(nil, 0644,
|
||||
WithOpener(func(_ []string, name string) (File, error) {
|
||||
return &FileConfig{
|
||||
tree := newTree(nil, 0644,
|
||||
withOpener(func(_ []string, name string) (File, error) {
|
||||
return &fileConfig{
|
||||
StatFn: func() (os.FileInfo, error) { return &SyntheticFileInfo{Name_: name}, nil },
|
||||
ReadFn: func() ([]byte, error) { return []byte("content"), nil },
|
||||
WriteFn: func([]byte) error { return nil },
|
||||
|
|
@ -77,7 +77,7 @@ func TestTreeOpen(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeOpenNotFound(t *testing.T) {
|
||||
tree := NewTree(nil, 0644)
|
||||
tree := newTree(nil, 0644)
|
||||
_, err := tree.Open("nonexistent")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
|
|
@ -85,8 +85,8 @@ func TestTreeOpenNotFound(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeStat(t *testing.T) {
|
||||
tree := NewTree(nil, 0755,
|
||||
WithStat(func(_ []string, name string) (os.FileInfo, error) {
|
||||
tree := newTree(nil, 0755,
|
||||
withStat(func(_ []string, name string) (os.FileInfo, error) {
|
||||
return &SyntheticFileInfo{Name_: name, Mode_: 0644, Size_: 42}, nil
|
||||
}),
|
||||
)
|
||||
|
|
@ -100,9 +100,9 @@ func TestTreeStat(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeStatOpenerFallback(t *testing.T) {
|
||||
tree := NewTree(nil, 0755,
|
||||
WithOpener(func(_ []string, name string) (File, error) {
|
||||
return &FileConfig{
|
||||
tree := newTree(nil, 0755,
|
||||
withOpener(func(_ []string, name string) (File, error) {
|
||||
return &fileConfig{
|
||||
StatFn: func() (os.FileInfo, error) {
|
||||
return &SyntheticFileInfo{Name_: name, Size_: 99}, nil
|
||||
},
|
||||
|
|
@ -122,21 +122,21 @@ func TestTreeStatOpenerFallback(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeChildDelegation(t *testing.T) {
|
||||
child := NewTree(nil, 0755,
|
||||
WithOpener(func(_ []string, name string) (File, error) {
|
||||
return &FileConfig{
|
||||
child := newTree(nil, 0755,
|
||||
withOpener(func(_ []string, name string) (File, error) {
|
||||
return &fileConfig{
|
||||
StatFn: func() (os.FileInfo, error) { return &SyntheticFileInfo{Name_: name}, nil },
|
||||
ReadFn: func() ([]byte, error) { return []byte("child:" + name), nil },
|
||||
WriteFn: func([]byte) error { return nil },
|
||||
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
|
||||
}, nil
|
||||
}),
|
||||
WithLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{FileEntry("inner", 0644)}, nil
|
||||
withLister(func(_ []string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{fileEntry("inner", 0644)}, nil
|
||||
}),
|
||||
)
|
||||
|
||||
parent := NewTree(nil, 0755)
|
||||
parent := newTree(nil, 0755)
|
||||
parent.Mount("sub", child)
|
||||
|
||||
// Open delegated to child
|
||||
|
|
@ -176,8 +176,8 @@ func TestTreeChildDelegation(t *testing.T) {
|
|||
|
||||
func TestTreeChildMkdirAll(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
child := NewTree([]string{tmpDir}, 0755)
|
||||
parent := NewTree(nil, 0755)
|
||||
child := newTree([]string{tmpDir}, 0755)
|
||||
parent := newTree(nil, 0755)
|
||||
parent.Mount("sub", child)
|
||||
|
||||
err := parent.MkdirAll("sub/new/deep/path")
|
||||
|
|
@ -192,8 +192,8 @@ func TestTreeChildMkdirAll(t *testing.T) {
|
|||
|
||||
func TestTreeCreate(t *testing.T) {
|
||||
created := ""
|
||||
tree := NewTree(nil, 0644,
|
||||
WithCreator(func(_ []string, name string, _ os.FileMode) error {
|
||||
tree := newTree(nil, 0644,
|
||||
withCreator(func(_ []string, name string, _ os.FileMode) error {
|
||||
created = name
|
||||
return nil
|
||||
}),
|
||||
|
|
@ -208,8 +208,8 @@ func TestTreeCreate(t *testing.T) {
|
|||
|
||||
func TestTreeDelete(t *testing.T) {
|
||||
deleted := ""
|
||||
tree := NewTree(nil, 0644,
|
||||
WithDeleter(func(_ []string, name string) error {
|
||||
tree := newTree(nil, 0644,
|
||||
withDeleter(func(_ []string, name string) error {
|
||||
deleted = name
|
||||
return nil
|
||||
}),
|
||||
|
|
@ -224,8 +224,8 @@ func TestTreeDelete(t *testing.T) {
|
|||
|
||||
func TestTreeRename(t *testing.T) {
|
||||
var oldN, newN string
|
||||
tree := NewTree(nil, 0644,
|
||||
WithRenamer(func(_ []string, old, new string) error {
|
||||
tree := newTree(nil, 0644,
|
||||
withRenamer(func(_ []string, old, new string) error {
|
||||
oldN, newN = old, new
|
||||
return nil
|
||||
}),
|
||||
|
|
@ -239,9 +239,9 @@ func TestTreeRename(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTreeReaddir(t *testing.T) {
|
||||
tree := NewTree(nil, 0755,
|
||||
WithReaddir(func(_ []string, name string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{FileEntry("child", 0644)}, nil
|
||||
tree := newTree(nil, 0755,
|
||||
withReaddir(func(_ []string, name string) ([]os.DirEntry, error) {
|
||||
return []os.DirEntry{fileEntry("child", 0644)}, nil
|
||||
}),
|
||||
)
|
||||
entries, err := tree.Readdir("subdir")
|
||||
|
|
@ -254,7 +254,7 @@ func TestTreeReaddir(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestReaddirNotFound(t *testing.T) {
|
||||
tree := NewTree(nil, 0755)
|
||||
tree := newTree(nil, 0755)
|
||||
_, err := tree.Readdir("nowhere")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
|
|
|
|||
Loading…
Reference in New Issue