···11+# Regenerate the lexicon bindings for both ends from the shared `lexicons/`
22+# corpus. Run after editing anything under `lexicons/`.
33+codegen: codegen-rust codegen-ts
44+55+# Rust backend bindings -> crates/knotty-lexicons/src
66+codegen-rust:
77+ cargo run -p knotty-lexgen
88+99+# SPA client bindings -> app/src/generated/lexicons
1010+codegen-ts:
1111+ pnpm --filter app run lexicon:generate
···11+[package]
22+name = "knotty-lexgen"
33+edition = "2024"
44+version.workspace = true
55+description.workspace = true
66+77+# Lexicon code generation for the Rust backend.
88+#
99+# Pinned to the same `jacquard-lexicon` minor as the `jacquard` runtime
1010+# (0.11): the generated code references `jacquard_common`/`jacquard_derive`
1111+# internals, so the generator and the runtime must agree on their API. The
1212+# published `jacquard-lexgen` binaries only exist for 0.12-beta, hence this
1313+# thin in-tree generator.
1414+[dependencies]
1515+jacquard-lexicon = "0.11"
1616+miette.workspace = true
+30
crates/knotty-lexicons/Cargo.toml
···11+[package]
22+name = "knotty-lexicons"
33+edition = "2024"
44+version.workspace = true
55+description.workspace = true
66+77+# Generated AT Protocol bindings for the `dev.drawbu.knotty.*` lexicons.
88+# Do not edit `src/` by hand: regenerate with `cargo run -p xtask`.
99+# Dependency set mirrors jacquard's own generated `jacquard-api` crate.
1010+1111+[lib]
1212+name = "knotty_lexicons"
1313+path = "src/lib.rs"
1414+1515+[features]
1616+default = ["std", "dev_drawbu"]
1717+std = ["serde/std", "jacquard-common/std"]
1818+dev_drawbu = []
1919+2020+[dependencies]
2121+jacquard-common = "0.11"
2222+jacquard-derive = "0.11"
2323+jacquard-lexicon = { version = "0.11", default-features = false }
2424+miette = "7.6"
2525+serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
2626+thiserror = { version = "2.0", default-features = false }
2727+2828+[lints.rust]
2929+non_snake_case = "allow"
3030+unused_imports = "allow"
+21
crates/knotty-lexgen/src/main.rs
···11+//! Generates the Rust lexicon bindings in `crates/knotty-lexicons` from the
22+//! shared `lexicons/` corpus. Run from the workspace root
33+44+use std::path::Path;
55+66+use jacquard_lexicon::codegen::{CodeGenerator, CodegenMode};
77+use jacquard_lexicon::corpus::LexiconCorpus;
88+99+const INPUT: &str = "lexicons";
1010+const OUTPUT: &str = "crates/knotty-lexicons/src";
1111+1212+fn main() -> miette::Result<()> {
1313+ let corpus = LexiconCorpus::load_from_dir(INPUT)?;
1414+ println!("loaded {} lexicon document(s) from {INPUT}", corpus.len());
1515+1616+ let codegen = CodeGenerator::with_mode(&corpus, "crate", CodegenMode::Pretty);
1717+ codegen.write_to_disk(Path::new(OUTPUT))?;
1818+ println!("generated bindings into {OUTPUT}");
1919+2020+ Ok(())
2121+}
+45
crates/knotty-lexicons/src/builder_types.rs
···11+// @generated by jacquard-lexicon. DO NOT EDIT.
22+//
33+// This file was automatically generated from Lexicon schemas.
44+// Any manual changes will be overwritten on the next regeneration.
55+66+/// Marker type indicating a builder field has been set
77+pub struct Set<T>(pub T);
88+impl<T> Set<T> {
99+ /// Extract the inner value
1010+ #[inline]
1111+ pub fn into_inner(self) -> T {
1212+ self.0
1313+ }
1414+}
1515+1616+/// Marker type indicating a builder field has not been set
1717+pub struct Unset;
1818+/// Trait indicating a builder field is set (has a value)
1919+2020+#[jacquard_common::deps::codegen::rustversion::attr(
2121+ since(1.78.0),
2222+ diagnostic::on_unimplemented(
2323+ message = "the field `{Self}` was not set, but this method requires it to be set",
2424+ label = "the field `{Self}` was not set"
2525+ )
2626+)]
2727+pub trait IsSet: private::Sealed {}
2828+/// Trait indicating a builder field is unset (no value yet)
2929+3030+#[jacquard_common::deps::codegen::rustversion::attr(
3131+ since(1.78.0),
3232+ diagnostic::on_unimplemented(
3333+ message = "the field `{Self}` was already set, but this method requires it to be unset",
3434+ label = "the field `{Self}` was already set"
3535+ )
3636+)]
3737+pub trait IsUnset: private::Sealed {}
3838+impl<T> IsSet for Set<T> {}
3939+impl IsUnset for Unset {}
4040+mod private {
4141+ /// Sealed trait to prevent external implementations
4242+ pub trait Sealed {}
4343+ impl<T> Sealed for super::Set<T> {}
4444+ impl Sealed for super::Unset {}
4545+}
+6
crates/knotty-lexicons/src/dev_drawbu.rs
···11+// @generated by jacquard-lexicon. DO NOT EDIT.
22+//
33+// This file was automatically generated from Lexicon schemas.
44+// Any manual changes will be overwritten on the next regeneration.
55+66+pub mod knotty;
+11
crates/knotty-lexicons/src/lib.rs
···11+// @generated by jacquard-lexicon. DO NOT EDIT.
22+//
33+// This file was automatically generated from Lexicon schemas.
44+// Any manual changes will be overwritten on the next regeneration.
55+66+extern crate alloc;
77+pub mod builder_types;
88+99+1010+#[cfg(feature = "dev_drawbu")]
1111+pub mod dev_drawbu;
+1
app/src/generated/lexicons/index.ts
···11+export * as DevDrawbuKnottyCreate from "./types/dev/drawbu/knotty/create.js";
+6
crates/knotty-lexicons/src/dev_drawbu/knotty.rs
···11+// @generated by jacquard-lexicon. DO NOT EDIT.
22+//
33+// This file was automatically generated from Lexicon schemas.
44+// Any manual changes will be overwritten on the next regeneration.
55+66+pub mod create;