ollie/fsedsl
Levi Neely 882fc598d4 Extract EDSL to fsedsl/ library
- Create fsedsl/ as embedded library with generic types:
  - FsNodeDecl[Ctx], Binding[Ctx], NodeOption[Ctx]
  - BuildTree[Ctx], GenerateHelp[Ctx]
  - Tree, File, FileConfig, SyntheticFileInfo

- Update ollie/fs to use fsedsl via type aliases:
  - type FsNodeDecl = fsedsl.FsNodeDecl[HandlerCtx]
  - Wrapper functions for constructors and options

- Migrate binding functions to Applier pattern:
  - Binding.Applier replaces direct field assignment
  - Cleanly separates EDSL mechanics from domain types

- Add replace directive in go.mod for local module

The library can be extracted to a separate repo later by
changing the module path and removing the replace directive.
2026-08-04 12:02:32 +02:00
..
LICENSE.md Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
README.md Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
builder.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
decl.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
decl_test.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
file.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
go.mod Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
help.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00
tree.go Extract EDSL to fsedsl/ library 2026-08-04 12:02:32 +02:00

README.md

fsedsl

A Go library providing an embedded domain-specific language (EDSL) for declaring synthetic filesystems. Designed for building 9P namespaces where the entire structure is declared statically but populated dynamically at runtime.

Features

  • Declarative filesystem specification: Define your namespace structure with DirNode, FileNode, and Each constructors
  • Generic context propagation: Type-safe context passing through the tree via Binding.Applier
  • Dynamic bindings: Each nodes iterate over runtime-determined collections
  • Auto-generated help: GenerateHelp produces documentation from Doc() annotations
  • Standard filesystem semantics: Implements read, write, stat, list, create, delete, rename

Installation

go get git.lneely.de/lkn/fsedsl

Quick Example

package main

import (
    "fmt"
    "git.lneely.de/lkn/fsedsl"
)

type Ctx struct {
    User *User
}

type User struct {
    ID   string
    Name string
}

var users = []*User{
    {ID: "1", Name: "alice"},
    {ID: "2", Name: "bob"},
}

func main() {
    spec := fsedsl.DirNode[Ctx]("/",
        fsedsl.FileNode[Ctx]("version", 0444,
            fsedsl.Doc[Ctx]("API version"),
            fsedsl.Read(func(ctx Ctx) ([]byte, error) {
                return []byte("1.0\n"), nil
            }),
        ),
        fsedsl.Each("{user}", func(ctx Ctx) ([]fsedsl.Binding[Ctx], error) {
            var out []fsedsl.Binding[Ctx]
            for _, u := range users {
                user := u // capture
                out = append(out, fsedsl.Binding[Ctx]{
                    Name:    user.Name,
                    Aliases: []string{user.ID},
                    Applier: func(c Ctx) Ctx {
                        c.User = user
                        return c
                    },
                })
            }
            return out, nil
        },
            fsedsl.FileNode[Ctx]("id", 0444,
                fsedsl.Read(func(ctx Ctx) ([]byte, error) {
                    return []byte(ctx.User.ID + "\n"), nil
                }),
            ),
            fsedsl.FileNode[Ctx]("name", 0666,
                fsedsl.Read(func(ctx Ctx) ([]byte, error) {
                    return []byte(ctx.User.Name + "\n"), nil
                }),
            ),
        ),
    )

    tree := fsedsl.BuildTree(spec, Ctx{})

    // List root
    entries, _ := tree.List()
    for _, e := range entries {
        fmt.Println(e.Name())
    }

    // Read a file
    f, _ := tree.Open("alice/name")
    data, _ := f.Read()
    fmt.Printf("alice/name: %s", data)
}

API Overview

Constructors

  • DirNode[Ctx](name, children/options...) — static directory
  • FileNode[Ctx](name, mode, options...) — file with handlers
  • Each[Ctx](pattern, bindingsFn, children/options...) — dynamic directory template

Options

  • Read(fn) — read handler
  • Write(fn) — write handler
  • Stream(fn) — streaming read (blocks indefinitely)
  • BlockOnce(fn) — blocking read (returns once)
  • Request(fn) — request/response pattern (rdwr)
  • Doc(desc) — human-readable description
  • UID(uid), GID(gid) — ownership
  • Alias(names...) — alternate lookup names

Building

tree := fsedsl.BuildTree(spec, rootCtx)

Help Generation

help := fsedsl.GenerateHelp(spec)

License

GPLv3