KDL
Basic Node with Properties
Simple struct with #[facet(kdl::property)] fields becomes KDL properties.
KDL Input
person name="Alice" age=30 email="alice@example.com"
Target Type
#[derive(Facet)] struct PersonDoc { #[facet(kdl::child)] person: Person, } #[derive(Facet)] struct Person { #[facet(kdl::property)] name: String, #[facet(kdl::property)] age: u32, #[facet(kdl::property)] email: Option<String>, }
Success
PersonDoc {
person: Person {
name: "Alice",
age: 30,
email: Option<String>::Some("alice@example.com"),
},
}
Node with Argument
#[facet(kdl::argument)] field becomes a positional argument after the node name.
Result: server "web-01" host="localhost" port=8080
KDL Input
server "web-01" host="localhost" port=8080
Target Type
#[derive(Facet)] struct ServerDoc { #[facet(kdl::child)] server: Server, } #[derive(Facet)] struct Server { #[facet(kdl::argument)] name: String, #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, }
Success
ServerDoc {
server: Server {
name: "web-01",
host: "localhost",
port: 8080,
},
}
Nested Nodes (Children)
#[facet(kdl::child)] fields become nested child nodes in braces.
The address struct becomes a child node of company.
KDL Input
company name="Acme Corp" { address street="123 Main St" city="Springfield" }
Target Type
#[derive(Facet)] struct CompanyDoc { #[facet(kdl::child)] company: Company, } #[derive(Facet)] struct Company { #[facet(kdl::property)] name: String, #[facet(kdl::child)] address: Address, } #[derive(Facet)] struct Address { #[facet(kdl::property)] street: String, #[facet(kdl::property)] city: String, }
Success
CompanyDoc {
company: Company {
name: "Acme Corp",
address: Address {
street: "123 Main St",
city: "Springfield",
},
},
}
Vec as Repeated Children
#[facet(kdl::children)] on a Vec field creates repeated child nodes.
Each Member becomes a separate member node.
KDL Input
member "Bob" role="Engineer" member "Carol" role="Designer" member "Dave" role="Manager"
Target Type
#[derive(Facet)] struct TeamDoc { #[facet(kdl::children)] member: Vec<Member>, } #[derive(Facet)] struct Member { #[facet(kdl::argument)] name: String, #[facet(kdl::property)] role: String, }
Success
TeamDoc {
member: Vec<Member> [
Member {
name: "Bob",
role: "Engineer",
},
Member {
name: "Carol",
role: "Designer",
},
Member {
name: "Dave",
role: "Manager",
},
],
}
Complex Nested Config
A realistic application config showing:
- Top-level properties (debug, features)
- Child nodes with arguments (server, database)
- Nested children (tls inside server)
- Optional children (tls is Option<TlsConfig>)
KDL Input
server "api-gateway" host="0.0.0.0" port=443 { tls cert_path="/etc/ssl/cert.pem" key_path="/etc/ssl/key.pem" } database "primary" url="postgres://localhost/mydb" pool_size=10
Target Type
#[derive(Facet)] struct AppConfig { #[facet(kdl::property)] debug: bool, #[facet(kdl::child)] server: ServerConfig, #[facet(kdl::child)] database: DatabaseConfig, #[facet(kdl::property)] features: Vec<String>, } #[derive(Facet)] struct DatabaseConfig { #[facet(kdl::argument)] name: String, #[facet(kdl::property)] url: String, #[facet(kdl::property)] pool_size: u32, } #[derive(Facet)] struct ServerConfig { #[facet(kdl::argument)] name: String, #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, #[facet(kdl::child)] tls: Option<TlsConfig>, } #[derive(Facet)] struct TlsConfig { #[facet(kdl::property)] cert_path: String, #[facet(kdl::property)] key_path: String, }
Error
kdl::invalid_document_shape
× invalid shape Undefined — needed struct with child/children fields
Roundtrip: Rust → KDL → Rust
Demonstrates serialization followed by deserialization.
The value survives the roundtrip intact.
KDL Input
config debug=#true max_connections=100 timeout_ms=5000
Target Type
#[derive(Facet)] struct ConfigDoc { #[facet(kdl::child)] config: Config, } #[derive(Facet)] struct Config { #[facet(kdl::property)] debug: bool, #[facet(kdl::property)] max_connections: u32, #[facet(kdl::property)] timeout_ms: u32, }
Success
ConfigDoc {
config: Config {
debug: true,
max_connections: 100,
timeout_ms: 5000,
},
}
Ambiguous Flattened Enum
Both TypeA and TypeB variants have identical fields (value, priority).
The solver cannot determine which variant to use.
KDL Input
resource "test" value="hello" priority=10
Target Type
#[derive(Facet)] struct AmbiguousConfig { #[facet(kdl::child)] resource: AmbiguousResource, } #[derive(Facet)] struct AmbiguousResource { #[facet(kdl::argument)] name: String, kind: AmbiguousKind, } #[derive(Facet)] #[repr(u8)] enum AmbiguousKind { TypeA(CommonFields), TypeB(CommonFields), } #[derive(Facet)] struct CommonFields { #[facet(kdl::property)] value: String, #[facet(kdl::property)] priority: u32, }
Error
kdl::solver
× Ambiguous: multiple resolutions match: ["AmbiguousKind::TypeA", "AmbiguousKind::TypeB"]
help: multiple variants match: AmbiguousKind::TypeA, AmbiguousKind::TypeB
use a KDL type annotation to specify the variant, e.g.: (VariantName)node-name ...
NoMatch with Per-Candidate Failures
Provide field names that don't exactly match any variant.
The solver shows WHY each candidate failed with 'did you mean?' suggestions.
KDL Input
backend "cache" hst="localhost" conn_str="pg"
Target Type
#[derive(Facet)] struct NoMatchConfig { #[facet(kdl::child)] backend: NoMatchBackend, } #[derive(Facet)] struct NoMatchBackend { #[facet(kdl::argument)] name: String, kind: NoMatchKind, } #[derive(Facet)] #[repr(u8)] enum NoMatchKind { Sqlite(SqliteBackend), Postgres(PostgresBackend), Redis(RedisBackend), } #[derive(Facet)] struct RedisBackend { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, #[facet(kdl::property)] password: Option<String>, } #[derive(Facet)] struct PostgresBackend { #[facet(kdl::property)] connection_string: String, #[facet(kdl::property)] pool_size: u32, } #[derive(Facet)] struct SqliteBackend { #[facet(kdl::property)] database_path: String, #[facet(kdl::property)] journal_mode: String, }
Error
kdl::solver
× No matching configuration for fields ["conn_str", "hst", "name"]
│
│ No variant matched:
│ - NoMatchKind::Redis: missing fields ["host", "port"], unknown fields ["conn_str", "hst"]
│ - NoMatchKind::Postgres: missing fields ["connection_string", "pool_size"], unknown fields ["conn_str", "hst"]
│ - NoMatchKind::Sqlite: missing fields ["database_path", "journal_mode"], unknown fields ["conn_str", "hst"]
│
│ Unknown fields: ["conn_str", "hst"]
│ Did you mean 'connection_string' instead of 'conn_str'?
│ Did you mean 'host' instead of 'hst'?
╭────
1 │ backend "cache" hst="localhost" conn_str="pg"
· ─┬─ ────┬───
· │ ╰── did you mean `connection_string`?
· ╰── did you mean `host`?
╰────
help: did you mean NoMatchKind::Redis?
all variants checked:
- NoMatchKind::Redis: missing host, port, unexpected conn_str, hst
- NoMatchKind::Postgres: missing connection_string, pool_size, unexpected conn_str, hst
- NoMatchKind::Sqlite: missing database_path, journal_mode, unexpected conn_str, hst
conn_str -> connection_string (did you mean connection_string?)
hst -> host (did you mean host?)
Unknown Fields with 'Did You Mean?' Suggestions
Misspell field names and see the solver suggest corrections!
Uses Jaro-Winkler similarity to find close matches.
KDL Input
server "web" hostnam="localhost" prot=8080
Target Type
#[derive(Facet)] struct TypoConfig { #[facet(kdl::child)] server: TypoServer, } #[derive(Facet)] struct TypoServer { #[facet(kdl::argument)] name: String, kind: TypoKind, } #[derive(Facet)] #[repr(u8)] enum TypoKind { Web(WebServer), Api(ApiServer), } #[derive(Facet)] struct ApiServer { #[facet(kdl::property)] endpoint: String, #[facet(kdl::property)] timeout_ms: u32, #[facet(kdl::property)] retry_count: u8, } #[derive(Facet)] struct WebServer { #[facet(kdl::property)] hostname: String, #[facet(kdl::property)] port: u16, #[facet(kdl::property)] ssl_enabled: bool, }
Error
kdl::solver
× No matching configuration for fields ["hostnam", "name", "prot"]
│
│ No variant matched:
│ - TypoKind::Web: missing fields ["hostname", "port", "ssl_enabled"], unknown fields ["hostnam", "prot"]
│ - TypoKind::Api: missing fields ["endpoint", "retry_count", "timeout_ms"], unknown fields ["hostnam", "prot"]
│
│ Unknown fields: ["hostnam", "prot"]
│ Did you mean 'hostname' instead of 'hostnam'?
│ Did you mean 'port' instead of 'prot'?
╭────
1 │ server "web" hostnam="localhost" prot=8080
· ───┬─── ──┬─
· │ ╰── did you mean `port`?
· ╰── did you mean `hostname`?
╰────
help: did you mean TypoKind::Web?
all variants checked:
- TypoKind::Web: missing hostname, port, ssl_enabled, unexpected hostnam, prot
- TypoKind::Api: missing endpoint, retry_count, timeout_ms, unexpected hostnam, prot
hostnam -> hostname (did you mean hostname?)
prot -> port (did you mean port?)
Value Overflow Detection
When a value doesn't fit ANY candidate type, the solver reports it.
count=5000000000 exceeds both u8 (max 255) and u32 (max ~4 billion).
KDL Input
data count=5000000000
Target Type
#[derive(Facet)] struct ValueConfig { #[facet(kdl::child)] data: ValueData, } #[derive(Facet)] struct ValueData { payload: ValuePayload, } #[derive(Facet)] #[repr(u8)] enum ValuePayload { Small(SmallValue), Large(LargeValue), } #[derive(Facet)] struct LargeValue { #[facet(kdl::property)] count: u32, } #[derive(Facet)] struct SmallValue { #[facet(kdl::property)] count: u8, }
Error
kdl::invalid_value
× invalid value for shape: value Integer(5000000000) doesn't fit any candidate type for field 'count'
Multi-Line Config with Typos
A more realistic multi-line configuration file with several typos.
Shows how the solver sorts candidates by closeness to the input.
KDL Input
database "production" \ hots="db.example.com" \ prot=3306 \ usernme="admin" \ pasword="secret123"
Target Type
#[derive(Facet)] struct MultiLineConfig { #[facet(kdl::child)] database: MultiLineDatabase, } #[derive(Facet)] struct MultiLineDatabase { #[facet(kdl::argument)] name: String, kind: MultiLineDbKind, } #[derive(Facet)] #[repr(u8)] enum MultiLineDbKind { MySql(MySqlConfig), Postgres(PgConfig), Mongo(MongoConfig), } #[derive(Facet)] struct MongoConfig { #[facet(kdl::property)] uri: String, #[facet(kdl::property)] replica_set: Option<String>, } #[derive(Facet)] struct PgConfig { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, #[facet(kdl::property)] database: String, #[facet(kdl::property)] ssl_mode: String, } #[derive(Facet)] struct MySqlConfig { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, #[facet(kdl::property)] username: String, #[facet(kdl::property)] password: String, }
Error
kdl::solver
× No matching configuration for fields ["hots", "name", "pasword", "prot", "usernme"]
│
│ No variant matched:
│ - MultiLineDbKind::MySql: missing fields ["host", "password", "port", "username"], unknown fields ["hots", "pasword", "prot", "usernme"]
│ - MultiLineDbKind::Postgres: missing fields ["database", "host", "port", "ssl_mode"], unknown fields ["hots", "pasword", "prot", "usernme"]
│ - MultiLineDbKind::Mongo: missing field 'uri', unknown fields ["hots", "pasword", "prot", "usernme"]
│
│ Unknown fields: ["hots", "pasword", "prot", "usernme"]
│ Did you mean 'host' instead of 'hots'?
│ Did you mean 'password' instead of 'pasword'?
│ Did you mean 'port' instead of 'prot'?
│ Did you mean 'username' instead of 'usernme'?
╭─[2:5]
1 │ database "production"
2 │ hots="db.example.com"
· ──┬─
· ╰── did you mean `host`?
3 │ prot=3306
· ──┬─
· ╰── did you mean `port`?
4 │ usernme="admin"
· ───┬───
· ╰── did you mean `username`?
5 │ pasword="secret123"
· ───┬───
· ╰── did you mean `password`?
╰────
help: did you mean MultiLineDbKind::MySql?
all variants checked:
- MultiLineDbKind::MySql: missing host, password, port, username, unexpected hots, pasword, prot, usernme
- MultiLineDbKind::Postgres: missing database, host, port, ssl_mode, unexpected hots, pasword, prot, usernme
- MultiLineDbKind::Mongo: missing uri, unexpected hots, pasword, prot, usernme
hots -> host (did you mean host?)
pasword -> password (did you mean password?)
prot -> port (did you mean port?)
usernme -> username (did you mean username?)
Unknown Field
KDL contains a property that doesn't exist in the target struct.
With #[facet(deny_unknown_fields)], this is an error.
KDL Input
server host="localhost" prot=8080
Target Type
#[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleConfig { #[facet(kdl::child)] server: SimpleServer, } #[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleServer { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, }
Error
kdl::unknown_property
× unknown property 'prot', expected one of: host, port
╭────
1 │ server host="localhost" prot=8080
· ──┬─
· ╰── unknown property `prot`
╰────
help: expected one of: host, port
Missing Required Field
KDL is missing a required field that has no default.
KDL Input
server host="localhost"
Target Type
#[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleConfig { #[facet(kdl::child)] server: SimpleServer, } #[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleServer { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, }
Error
kdl::reflect
× Field 'SimpleServer::port' was not initialized
Syntax Error: Unquoted Boolean
KDL 2.0 requires booleans to be written as #true/#false.
Bare true or false is a syntax error with a helpful message.
KDL Input
server host="localhost" enabled=true
Target Type
#[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleConfig { #[facet(kdl::child)] server: SimpleServer, } #[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleServer { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, }
Error
kdl::parse
× Failed to parse KDL document
Error:
× Expected identifier string
╭────
1 │ server host="localhost" enabled=true
· ──┬─
· ╰── not identifier string
╰────
Syntax Error: Unclosed Brace
Missing closing brace in nested node structure.
The parser provides line/column information for the error.
KDL Input
server host="localhost" port=8080 { tls cert="/path/to/cert"
Target Type
#[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleConfig { #[facet(kdl::child)] server: SimpleServer, } #[derive(Facet)] #[facet(deny_unknown_fields)] struct SimpleServer { #[facet(kdl::property)] host: String, #[facet(kdl::property)] port: u16, }
Error
kdl::parse
× Failed to parse KDL document
Error:
× No closing '}' for child block
╭─[1:35]
1 │ server host="localhost" port=8080 {
· ┬
· ╰── not closed
2 │ tls cert="/path/to/cert"
╰────
Error:
× Closing '}' was not found after nodes
╭─[1:36]
1 │ ╭─▶ server host="localhost" port=8080 {
2 │ ├─▶ tls cert="/path/to/cert"
· ╰──── not closed
╰────