# virtfs A Go library for declaring synthetic filesystems. Define your namespace structure statically, populate it dynamically at runtime with closures. Produces a `*Tree` usable with any filesystem protocol (9P, FUSE, etc.) or as an in-memory filesystem for testing. ## Design Handlers are plain closures that capture their state at binding time. There is no context type parameter — each handler already knows its target via closure capture. Dynamic subtrees use `Each`: a bindings function returns `[]Binding`, where each binding carries pre-bound `Children` (fully-wired subtrees with their own closures). ## Quick Start ```go package main import ( "fmt" "ollie/virtfs" ) type User struct { ID string Name string } var users = []*User{ {ID: "1", Name: "alice"}, {ID: "2", Name: "bob"}, } func main() { spec := virtfs.DirNode("/", virtfs.FileNode("version", 0444, virtfs.Doc("API version"), virtfs.Read(func() ([]byte, error) { return []byte("1.0\n"), nil }), ), virtfs.Each("{user}", func() ([]virtfs.Binding, error) { var out []virtfs.Binding for _, u := range users { user := u // capture out = append(out, virtfs.Binding{ Name: user.Name, Aliases: []string{user.ID}, Children: []virtfs.FsNodeDecl{ virtfs.FileNode("id", 0444, virtfs.Read(func() ([]byte, error) { return []byte(user.ID + "\n"), nil }), ), virtfs.FileNode("name", 0444, virtfs.Read(func() ([]byte, error) { return []byte(user.Name + "\n"), nil }), ), }, }) } return out, nil }), ) tree := virtfs.BuildTree(spec) entries, _ := tree.List() for _, e := range entries { fmt.Println(e.Name()) } f, _ := tree.Open("alice/name") data, _ := f.Read() fmt.Printf("alice/name: %s", data) } ``` ## API ### Constructors | Function | Description | |----------|-------------| | `DirNode(name, children/options...)` | Static directory | | `FileNode(name, mode, options...)` | File with handlers | | `Each(name, bindingsFn, options...)` | Dynamic directory — children come from bindings | ### Options (NodeOption) | Option | Signature | Description | |--------|-----------|-------------| | `Read(fn)` | `func() ([]byte, error)` | Non-blocking read | | `Write(fn)` | `func([]byte) error` | Write handler | | `Stream(fn)` | `func(context.Context, string) ([]byte, string, error)` | Blocking streaming read | | `BlockOnce(fn)` | `func(context.Context, string) ([]byte, string, error)` | Blocking read, returns once | | `Request(fn)` | `func([]byte) ([]byte, error)` | Write-then-read (rdwr) | | `Doc(desc)` | `string` | Human-readable description | | `UID(uid)` | `string` | Set owner | | `GID(gid)` | `string` | Set group | | `StatOverride(fn)` | `func() os.FileInfo` | Custom stat | | `RemoveNode(fn)` | `func() error` | Removal handler | ### Binding Returned by the `Each` bindings function. Each binding represents one dynamic child. ```go virtfs.Binding{ Name: "alice", Aliases: []string{"user-id-123"}, Children: []virtfs.FsNodeDecl{ ... }, // pre-bound subtree Remove: func() { ... }, // optional Rename: func(newName string) error { ... }, // optional } ``` Children are fully-wired — their handlers are closures that already captured the specific instance (no applier/context needed). ### Tree Operations ```go tree := virtfs.BuildTree(spec) tree.List() // list root entries tree.Stat(name) // stat a path tree.Open(name) // open file for read/write tree.Create(name) // create file tree.Delete(name) // remove file tree.Rename(old, new) // rename file tree.Readdir(path) // list subdirectory ``` ### Help Generation ```go help := virtfs.GenerateHelp(spec) // Produces formatted docs from Doc() annotations ``` ## License GPLv3