diff --git a/virtfs/builder.go b/virtfs/builder.go index 6ccd1d3..22d43a5 100644 --- a/virtfs/builder.go +++ b/virtfs/builder.go @@ -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 ──────────────────────────────────────────────── diff --git a/virtfs/decl_test.go b/virtfs/decl_test.go index ca82476..1bf7473 100644 --- a/virtfs/decl_test.go +++ b/virtfs/decl_test.go @@ -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") diff --git a/virtfs/tree.go b/virtfs/tree.go index d74f0e6..4a4579b 100644 --- a/virtfs/tree.go +++ b/virtfs/tree.go @@ -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 { diff --git a/virtfs/tree_test.go b/virtfs/tree_test.go index eb3d224..5d1af4c 100644 --- a/virtfs/tree_test.go +++ b/virtfs/tree_test.go @@ -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")