Expand description
fig-schema — the schema layer: what a field expects — its type, its
allowed values, and how to present it — layered over fig’s schema-free
value tree.
fig parses bytes → fig::Value and edits losslessly; it has no notion of
“what is valid here”. This crate adds that knowledge as a generic,
embedder-agnostic engine. It never learns the word “prov” or “flower”: a
consumer (prov, for frontmatter fields; flower, for a metadata editor)
defines its own constraint type — an enum covering whatever kinds of
constraint it needs (a controlled vocabulary, a reference into a
workspace, …) — and implements Validate on it. FieldRule/Schema
are generic over that type, so the path-matching and commit-time
validation plumbing is written once, here, and reused by every embedder.
What’s genuinely reusable, and lives here as concrete types rather than being left to the embedder:
-
PathPat/SegPat— pattern-matching a fig path, including “every item of this list” (SegPat::EachItem) and “this subtree” (SegPat::AnyDepth). -
FieldType— the expected type, and type-directed coercion of an edit buffer (FieldType::coerce). -
Term/Cardinality/validate_enum— a controlled vocabulary and the logic to check a value against one (closed-vocabulary rejection, open-vocabulary near-miss warnings). Cardinality (one vs. many) is pure data shape, useful even to a constraint this crate doesn’t otherwise model (a relation/reference field, for instance). -
VocabularyDoc/parse_vocabulary— load a term set from the sharedvocabulary: { field, values }/terms:document convention, so independent embedders can point at the same vocabulary document without either depending on the other. -
Presentation/Icon/Tint— renderer-neutral display hints, carried on every rule but never interpreted here. -
Consequence/Severity— what changing a field costs, so a host can warn before committing an expensive or irreversible edit. A separate fact from aTint, which says only how loudly to draw the field. -
Issue/IssueKind— why a value failed, as data rather than prose, so the embedder owns the wording.Issue’sDisplayrenders a reasonable English default for embedders that don’t care.
The public structs are #[non_exhaustive], so they are built from a
constructor plus chainable setters (FieldRule::new, Term::value,
Presentation::default) rather than a struct literal. That is what lets a
later release add a hint without costing every embedder a major version.
§Example
use fig::Value;
use fig_schema::{
Consequence, FieldRule, FieldType, PathPat, Presentation, Schema, Seg, Severity,
Term, Validate, Validation, validate_enum,
};
// The embedder's own constraint type — the seam this crate is built around.
struct Vocabulary { values: Vec<Term>, closed: bool }
impl Validate for Vocabulary {
fn validate(&self, value: &Value) -> Validation {
validate_enum(&self.values, self.closed, value)
}
}
let schema = Schema::new(vec![
FieldRule::new(PathPat::each_item_of("audience"))
.ty(FieldType::Str)
.constraint(Vocabulary {
values: vec![Term::value("public"), Term::value("family")],
closed: true,
})
.present(Presentation::default().title("Audience"))
.on_change(
Consequence::when("public", "Anyone with the link will be able to read this.")
.severity(Severity::Confirm),
),
]);
// Find the rule governing `audience[0]`, then check a candidate against it.
let path = [Seg::Key("audience".into()), Seg::Index(0)];
let rule = schema.rule_for(&path).expect("a rule governs this path");
assert!(rule.validate(&Value::Str("public".into())).is_ok());
// Valid, but not free — ask before committing it.
assert_eq!(
rule.severity_of(&Value::Str("public".into())),
Some(Severity::Confirm),
);
assert_eq!(rule.severity_of(&Value::Str("family".into())), None);
let rejected = rule.validate(&Value::Str("familly".into()));
assert!(rejected.is_reject());
assert_eq!(
rejected.issue().unwrap().suggestion.as_deref(),
Some("family"),
);Structs§
- Consequence
- A cost of changing a field, declared on the rule that governs it.
- Field
Rule - One field rule: which node(s) it governs, the type it expects, an optional
constraint of the embedder’s own type
C, and how to present it. - Issue
- Why a value failed to validate.
- PathPat
- A path pattern. Unlike a concrete
Segpath it can reach every element of a sequence (SegPat::EachItem), every entry of a mapping (SegPat::AnyKey), or a whole subtree (SegPat::AnyDepth), so a rule can constrain each item of a list field (tags:,audience:) or everything beneath a key. - Presentation
- Presentation hints for one field rule.
- Schema
- A set of field rules. Matched against a row’s fig path to find what governs it.
- Term
- One term of a controlled vocabulary.
- Vocabulary
Doc - A controlled vocabulary loaded from its own document: the
fieldit governs, whether its value set is closed, and the terms themselves — ready to hand tovalidate_enum.
Enums§
- Cardinality
- Whether a reference or list-shaped field holds one entry or many. Pure data
shape — reused by an embedder’s own reference/relation constraint (prov’s
spanning/cardinalityconcepts, for instance) without this crate needing to know what a “relation” is. - Field
Type - The type a field expects. Drives type-directed parsing and widget choice.
- Icon
- A semantic icon hint. Frontends map to their own symbol set.
- Issue
Kind - The kind of an
Issue. - Seg
- One step of a fig path: a mapping key or a sequence index. Owned (unlike
fig::Segment<'a>, which borrows), so a path can outlive a single FFI call. - SegPat
- One step of a
PathPat. - Severity
- What the host should do about a consequence — the interaction, not a
measure of how bad it is. Mirrors
Validation’s Ok/Warn/Reject: the crate names the response, the embedder renders it. - Tint
- A semantic tint hint. Frontends map to theme-adaptive colours.
- Validation
- The result of validating a value against a field’s constraint at commit time.
Traits§
- Validate
- A field constraint that knows how to check a candidate value. An embedder
implements this on its own constraint type (an enum with a vocabulary
variant, a reference variant, whatever it needs);
crate::FieldRule::validatedispatches to it generically.
Functions§
- guards_
without_ terms - Guards that name a value the vocabulary doesn’t have — a lint, run when an embedder loads its schema, not part of validation.
- parse_
vocabulary - Parse a vocabulary document from its top-level value: a
vocabulary: { field, values }marker plus aterms:mapping, each entry either a bare key (a live term with no metadata) or a{ label?, description?, retired? }mapping. ReturnsNonewhenvaluecarries novocabularymarker — i.e. it is not a vocabulary document. - validate_
enum - Validate
valueagainst a controlled vocabulary — the reusable logic behind any embedder’s vocabulary-shaped constraint.