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.docis per field because that is what:describe-optionand:customizerender beside it. - Field
Node - A
Fieldwith its schema as an index. - Shape
Error - A failed
ConfigShape::from_value, carrying where.
Enums§
- Scalar
Kind - The leaf kinds. Mirrors
lattice_config::ScalarKindand the WIToption-type. - Schema
- The declared shape of a value. Mirrors
lattice_config::ConfigSchema. - Schema
Node - One node of a flattened
Schema. Child links are indices into the node listflatten_schemareturns. - Value
- A value shaped by a
Schema. Mirrorslattice_config::ConfigValue. - Value
Node - One node of a flattened
Value.
Traits§
- Config
Shape - 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). Seeflatten_schema. - unflatten_
value - Rebuild a
Valuefrom an arena — the read direction, for a guest turning aget-option-valueanswer back into a tree beforefrom_value.