Skip to main content

Module shape

Module shape 

Source
Expand description

A Rust struct as a config schema and a config value, plus the arena flattening the WIT seam needs (slice TC.4).

Design: docs/dev/architecture/typed-configuration.md.

TC.3 gave the ABI a way to carry structured configuration: an option declares a schema, values cross as a tree, the host validates one against the other. It also made both cross as an arena — a flat node list plus a root index — because WIT has no recursive types. Writing an arena by hand is exactly as unpleasant as it sounds; the config-guest fixture does it once, deliberately, to prove the encoding, and no real plugin should.

This module is what a real plugin uses instead. #[derive(ConfigShape)] on an ordinary struct produces ConfigShape::schema, ConfigShape::to_value and ConfigShape::from_value; flatten_schema / flatten_value turn either into an arena.

WIT-agnostic, like the rest of the SDK. The types here are the SDK’s own mirrors, not the generated bindings — a proc-macro crate cannot name a per-world WIT type, which is why #[derive(PluginOption)] hands back an OptionKind the plugin maps at the call site. The same one-line-per-plugin tax applies here: a plugin writes one fn mapping SchemaNode / ValueNode to its generated node types, once, not per option.

The design claim this slice makes true is that a guest’s parse becomes total and mechanical — a walk over a tree the host has already validated — where before it was a bespoke text parser per option. Without the derive that claim is aspirational and the tree is simply a worse blob.

§Examples

A struct describes itself, round-trips through a Value, and flattens to the arena form that crosses the WIT boundary:

use lattice_plugin_sdk::ConfigShape;
use lattice_plugin_sdk::shape::{
    ConfigShape as _, Schema, Value, flatten_value, unflatten_value,
};

/// Where a capture lands.
#[derive(Debug, PartialEq, ConfigShape)]
struct Target {
    /// The file it is appended to.
    file: String,
    /// Insert under this headline instead of appending.
    headline: Option<String>,
}

// The schema: a record, fields kebab-cased, `Option<T>` = not required.
let Schema::Record(fields) = Target::schema() else { unreachable!() };
assert_eq!(fields[0].name, "file");
assert!(fields[0].required);
assert!(!fields[1].required);
assert_eq!(fields[1].doc, "Insert under this headline instead of appending.");

// The value: an absent optional field is omitted, not emitted empty.
let t = Target { file: "refile.org".into(), headline: None };
let v = t.to_value();
assert_eq!(v.field("file").and_then(Value::as_str), Some("refile.org"));
assert_eq!(v.field("headline"), None);
assert_eq!(Target::from_value(&v), Ok(t));

// The arena: what actually crosses the boundary, and back.
let (nodes, root) = flatten_value(&v);
assert_eq!(unflatten_value(&nodes, root), Ok(v));

Structs§

Field
One field of a Schema::Record. doc is per field because that is what :describe-option and :customize render beside it.
FieldNode
A Field with its schema as an index.
ShapeError
A failed ConfigShape::from_value, carrying where.

Enums§

ScalarKind
The leaf kinds. Mirrors lattice_config::ScalarKind and the WIT option-type.
Schema
The declared shape of a value. Mirrors lattice_config::ConfigSchema.
SchemaNode
One node of a flattened Schema. Child links are indices into the node list flatten_schema returns.
Value
A value shaped by a Schema. Mirrors lattice_config::ConfigValue.
ValueNode
One node of a flattened Value.

Traits§

ConfigShape
A type that can describe its own configuration shape and move through it.

Functions§

flatten_schema
Flatten a schema to (nodes, root).
flatten_value
Flatten a value to (nodes, root). See flatten_schema.
unflatten_value
Rebuild a Value from an arena — the read direction, for a guest turning a get-option-value answer back into a tree before from_value.