81 lines
2.5 KiB
Go
81 lines
2.5 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// FileSpec describes a single synthetic file exposed by a DirStore.
|
|
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
|
|
}
|
|
|
|
// NewFileStore creates a DirStore backed by a table of FileSpecs (synthetic files).
|
|
func NewFileStore(specs []FileSpec) *DirStore {
|
|
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 NewDirStore(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) (StoreEntry, 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 &EntryConfig{
|
|
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
|
|
}),
|
|
)
|
|
}
|