This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-9p/fs/synth.go

81 lines
2.4 KiB
Go

package fs
import (
"context"
"fmt"
"os"
)
// FileSpec describes a single synthetic file exposed by a Tree.
type FileSpec struct {
Name string
Mode os.FileMode
Read func() ([]byte, error)
Write func([]byte) error // nil = read-only
Wait func(ctx context.Context, base string) (content []byte, nextBase string, err error) // nil = not waitable
Size func() int64 // optional; if nil, len(Read())
OneShot bool // true = yields data once per open
Async bool // true = writes dispatched asynchronously
}
// NewSynth creates a Tree backed by a table of FileSpecs (synthetic files).
func NewSynth(specs []FileSpec) *Tree {
index := make(map[string]int, len(specs))
for i, s := range specs {
index[s.Name] = i
}
lookup := func(name string) (*FileSpec, bool) {
i, ok := index[name]
if !ok {
return nil, false
}
return &specs[i], true
}
return NewTree(nil, 0,
WithLister(func(_ []string) ([]os.DirEntry, error) {
entries := make([]os.DirEntry, len(specs))
for i, s := range specs {
entries[i] = FileEntry(s.Name, s.Mode)
}
return entries, nil
}),
WithOpener(func(_ []string, name string) (File, error) {
spec, ok := lookup(name)
if !ok {
return nil, fmt.Errorf("%s: not found", name)
}
writeFn := func([]byte) error { return fmt.Errorf("%s: read-only", name) }
if spec.Write != nil {
writeFn = spec.Write
}
waitFn := func(context.Context, string) ([]byte, string, error) {
return nil, "", fmt.Errorf("%s: not a wait file", name)
}
if spec.Wait != nil {
waitFn = spec.Wait
}
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
var size int64
if spec.Size != nil {
size = spec.Size()
} else if spec.Wait != nil {
size = 4096
} else if data, err := spec.Read(); err == nil {
size = int64(len(data))
}
return &SyntheticFileInfo{Name_: name, Mode_: spec.Mode, Size_: size}, nil
},
ReadFn: spec.Read,
WriteFn: writeFn,
BlockingReadFn: waitFn,
OneShot_: spec.OneShot || spec.Wait != nil,
IsBlocking_: spec.Wait != nil,
Async_: spec.Async,
}, nil
}),
)
}