virtfs: inline tree options, remove newTree/with* indirection

The with* functional options and newTree constructor were private and
only used in two places (builder and tests). Replaced with direct
field assignment on &Tree{} literals. Less abstraction, same behavior.
This commit is contained in:
Ollie Agent 2026-08-11 20:28:18 +02:00
parent 5a09e603cd
commit a40e1dbed0
4 changed files with 104 additions and 163 deletions

View File

@ -81,29 +81,28 @@ func build(d *FsNodeDecl, parentUid, parentGid string) *Tree {
mode = 0755
}
t := newTree(nil, mode,
withLister(func(_ []string) ([]os.DirEntry, error) {
return listDir(d, uid, gid)
}),
withReaddir(func(_ []string, name string) ([]os.DirEntry, error) {
return readdirDir(d, uid, gid, name)
}),
withStat(func(_ []string, name string) (os.FileInfo, error) {
return statDir(d, uid, gid, name)
}),
withOpener(func(_ []string, name string) (File, error) {
return openDir(d, uid, gid, name)
}),
withDeleter(func(_ []string, name string) error {
return deleteDir(d, name)
}),
withCreator(func(_ []string, name string, _ os.FileMode) error {
return fmt.Errorf("create not supported")
}),
withRenamer(func(_ []string, old, new string) error {
return renameDir(d, old, new)
}),
)
t := &Tree{perm: mode}
t.listFn = func(_ []string) ([]os.DirEntry, error) {
return listDir(d, uid, gid)
}
t.readdirFn = func(_ []string, name string) ([]os.DirEntry, error) {
return readdirDir(d, uid, gid, name)
}
t.statFn = func(_ []string, name string) (os.FileInfo, error) {
return statDir(d, uid, gid, name)
}
t.openFn = func(_ []string, name string) (File, error) {
return openDir(d, uid, gid, name)
}
t.deleteFn = func(_ []string, name string) error {
return deleteDir(d, name)
}
t.createFn = func(_ []string, name string, _ os.FileMode) error {
return fmt.Errorf("create not supported")
}
t.renameFn = func(_ []string, old, new string) error {
return renameDir(d, old, new)
}
return t
}
@ -111,14 +110,14 @@ func build(d *FsNodeDecl, parentUid, parentGid string) *Tree {
if mode == 0 {
mode = 0444
}
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) {
return openLeaf(d, uid, gid, mode, name)
}),
)
t := &Tree{}
t.listFn = func(_ []string) ([]os.DirEntry, error) {
return nil, fmt.Errorf("%s: is a file", d.Name)
}
t.openFn = func(_ []string, name string) (File, error) {
return openLeaf(d, uid, gid, mode, name)
}
return t
}
// ── directory ops ────────────────────────────────────────────────

View File

@ -85,12 +85,11 @@ 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 := &Tree{perm: 0755}
child := &Tree{perm: 0755}
child.listFn = func(_ []string) ([]os.DirEntry, error) {
return []os.DirEntry{fileEntry("childfile", 0444)}, nil
}
parent.Mount("subdir", child)
@ -111,8 +110,8 @@ func TestTreeMount(t *testing.T) {
}
func TestTreeSplit(t *testing.T) {
parent := newTree(nil, 0755)
child := newTree(nil, 0755)
parent := &Tree{perm: 0755}
child := &Tree{perm: 0755}
parent.Mount("sub", child)
c, rest, ok := parent.split("sub")

View File

@ -33,54 +33,6 @@ type Tree struct {
Data any // user-attached state
}
// 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 {
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 {
return func(d *Tree) { d.createFn = fn }
}
// 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 {
return func(d *Tree) { d.renameFn = fn }
}
// 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 {
return func(d *Tree) { d.statFn = fn }
}
// 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
// is provided via options.
func newTree(dirs []string, perm os.FileMode, opts ...treeOption) *Tree {
d := &Tree{Tree: dirs, perm: perm}
for _, o := range opts {
o(d)
}
return d
}
// Mount attaches a child Tree at the given name.
func (d *Tree) Mount(name string, child *Tree) {
if d.children == nil {

View File

@ -9,7 +9,7 @@ import (
func TestTreeMkdirAll(t *testing.T) {
tmpDir := t.TempDir()
tree := newTree([]string{tmpDir}, 0755)
tree := &Tree{Tree: []string{tmpDir}, perm: 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 := &Tree{perm: 0755}
err := tree.MkdirAll("somedir")
if err == nil {
t.Fatal("expected error for nil backing dir")
@ -34,12 +34,11 @@ func TestTreeMkdirAllNoBackingDir(t *testing.T) {
func TestTreeWithCustomFunctions(t *testing.T) {
listed := false
tree := newTree(nil, 0755,
withLister(func(_ []string) ([]os.DirEntry, error) {
listed = true
return []os.DirEntry{fileEntry("a", 0644)}, nil
}),
)
tree := &Tree{perm: 0755}
tree.listFn = func(_ []string) ([]os.DirEntry, error) {
listed = true
return []os.DirEntry{fileEntry("a", 0644)}, nil
}
entries, err := tree.List()
if err != nil {
t.Fatalf("List: %v", err)
@ -53,16 +52,15 @@ func TestTreeWithCustomFunctions(t *testing.T) {
}
func TestTreeOpen(t *testing.T) {
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 },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}),
)
tree := &Tree{perm: 0644}
tree.openFn = 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 },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}
f, err := tree.Open("test")
if err != nil {
t.Fatalf("Open: %v", err)
@ -77,7 +75,7 @@ func TestTreeOpen(t *testing.T) {
}
func TestTreeOpenNotFound(t *testing.T) {
tree := newTree(nil, 0644)
tree := &Tree{perm: 0644}
_, err := tree.Open("nonexistent")
if err == nil {
t.Fatal("expected error")
@ -85,11 +83,10 @@ func TestTreeOpenNotFound(t *testing.T) {
}
func TestTreeStat(t *testing.T) {
tree := newTree(nil, 0755,
withStat(func(_ []string, name string) (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: name, Mode_: 0644, Size_: 42}, nil
}),
)
tree := &Tree{perm: 0755}
tree.statFn = func(_ []string, name string) (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: name, Mode_: 0644, Size_: 42}, nil
}
fi, err := tree.Stat("hello")
if err != nil {
t.Fatalf("Stat: %v", err)
@ -100,18 +97,17 @@ func TestTreeStat(t *testing.T) {
}
func TestTreeStatOpenerFallback(t *testing.T) {
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
},
ReadFn: func() ([]byte, error) { return nil, nil },
WriteFn: func([]byte) error { return nil },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}),
)
tree := &Tree{perm: 0755}
tree.openFn = func(_ []string, name string) (File, error) {
return &fileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: name, Size_: 99}, nil
},
ReadFn: func() ([]byte, error) { return nil, nil },
WriteFn: func([]byte) error { return nil },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}
fi, err := tree.Stat("x")
if err != nil {
t.Fatalf("Stat: %v", err)
@ -122,21 +118,20 @@ func TestTreeStatOpenerFallback(t *testing.T) {
}
func TestTreeChildDelegation(t *testing.T) {
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
}),
)
child := &Tree{perm: 0755}
child.openFn = 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
}
child.listFn = func(_ []string) ([]os.DirEntry, error) {
return []os.DirEntry{fileEntry("inner", 0644)}, nil
}
parent := newTree(nil, 0755)
parent := &Tree{perm: 0755}
parent.Mount("sub", child)
// Open delegated to child
@ -176,8 +171,8 @@ func TestTreeChildDelegation(t *testing.T) {
func TestTreeChildMkdirAll(t *testing.T) {
tmpDir := t.TempDir()
child := newTree([]string{tmpDir}, 0755)
parent := newTree(nil, 0755)
child := &Tree{Tree: []string{tmpDir}, perm: 0755}
parent := &Tree{perm: 0755}
parent.Mount("sub", child)
err := parent.MkdirAll("sub/new/deep/path")
@ -192,12 +187,11 @@ func TestTreeChildMkdirAll(t *testing.T) {
func TestTreeCreate(t *testing.T) {
created := ""
tree := newTree(nil, 0644,
withCreator(func(_ []string, name string, _ os.FileMode) error {
created = name
return nil
}),
)
tree := &Tree{perm: 0644}
tree.createFn = func(_ []string, name string, _ os.FileMode) error {
created = name
return nil
}
if err := tree.Create("newfile"); err != nil {
t.Fatalf("Create: %v", err)
}
@ -208,12 +202,11 @@ func TestTreeCreate(t *testing.T) {
func TestTreeDelete(t *testing.T) {
deleted := ""
tree := newTree(nil, 0644,
withDeleter(func(_ []string, name string) error {
deleted = name
return nil
}),
)
tree := &Tree{perm: 0644}
tree.deleteFn = func(_ []string, name string) error {
deleted = name
return nil
}
if err := tree.Delete("old"); err != nil {
t.Fatalf("Delete: %v", err)
}
@ -224,12 +217,11 @@ 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 {
oldN, newN = old, new
return nil
}),
)
tree := &Tree{perm: 0644}
tree.renameFn = func(_ []string, old, new string) error {
oldN, newN = old, new
return nil
}
if err := tree.Rename("a", "b"); err != nil {
t.Fatalf("Rename: %v", err)
}
@ -239,11 +231,10 @@ 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 := &Tree{perm: 0755}
tree.readdirFn = func(_ []string, name string) ([]os.DirEntry, error) {
return []os.DirEntry{fileEntry("child", 0644)}, nil
}
entries, err := tree.Readdir("subdir")
if err != nil {
t.Fatalf("Readdir: %v", err)
@ -254,7 +245,7 @@ func TestTreeReaddir(t *testing.T) {
}
func TestReaddirNotFound(t *testing.T) {
tree := newTree(nil, 0755)
tree := &Tree{perm: 0755}
_, err := tree.Readdir("nowhere")
if err == nil {
t.Fatal("expected error")