### KDL Example Structure Source: https://github.com/tailhook/knuffel/blob/main/README.md A basic KDL file structure demonstrating nodes, arguments, properties, and nested blocks. ```kdl foo 1 key="val" "three" { bar (role)baz 1 2 } ``` -------------------------------- ### Define a node with a type name Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Example of a KDL node using a type name in parentheses. ```kdl (text)document name="New Document" { } ``` -------------------------------- ### Root Document Structure Example Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Demonstrates a struct that can serve as the root document, containing children and arguments. Fields marked only with `child`, `children`, or unmarked fields are permissible for root structures. ```rust # #[derive(knuffel::Decode)] # struct NamedNode { #[knuffel(argument)] name: u32 } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(child, unwrap(argument))] version: u32, #[knuffel(children(name="plugin"))] plugins: Vec, #[knuffel(children(name="datum"))] data: Vec, } ``` -------------------------------- ### KDL input for scalar enums Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode_scalar.md Example KDL configuration that matches the defined scalar enum variants in kebab-case. ```kdl all-colors "red" "blue" "green" "infra-red" ``` -------------------------------- ### Parse String and Bytes with Knuffel Source: https://context7.com/tailhook/knuffel/llms.txt Use `str` for FromStr parsing and `bytes` for binary data, including base64. This example demonstrates parsing network and secret configurations. ```rust use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; #[derive(knuffel::Decode, Debug)] struct NetworkConfig { #[knuffel(property, str)] bind_ip: IpAddr, #[knuffel(property, str)] listen: SocketAddr, #[knuffel(property, str)] config_path: PathBuf, } #[derive(knuffel::Decode, Debug)] struct SecretConfig { #[knuffel(child, unwrap(argument, bytes))] api_key: Vec, #[knuffel(child, unwrap(argument, bytes))] certificate: Vec, } fn main() -> miette::Result<()> { let network: Vec = knuffel::parse("network.kdl", r#" config bind-ip="192.168.1.1" listen="0.0.0.0:8080" config-path="/etc/app/config.toml" "#)?; assert_eq!(network[0].bind_ip.to_string(), "192.168.1.1"); assert_eq!(network[0].listen.port(), 8080); // bytes can be plain strings (UTF-8) or base64 encoded let secrets: SecretConfig = knuffel::parse("secrets.kdl", r#" api-key "my-secret-key" certificate (base64)"SGVsbG8gV29ybGQh" "#)?; assert_eq!(secrets.api_key, b"my-secret-key"); assert_eq!(secrets.certificate, b"Hello World!"); Ok(()) } ``` -------------------------------- ### Define KDL Node Hierarchy Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Example of KDL nodes with identifiers, types, and nested children. ```kdl node1 "x" "y" (my_type)node2 prop="value" { node3 1 node4 2 } ``` -------------------------------- ### Inspect KDL Document AST Source: https://context7.com/tailhook/knuffel/llms.txt Work with parsed KDL documents at a lower level using the AST module. This example demonstrates iterating through nodes, arguments, properties, and children of a document. ```rust use knuffel::span::Span; use knuffel::ast::{Document, Node, Literal, Value}; fn inspect_document(doc: &Document) { for node in &doc.nodes { println!("Node: {}", &*node.node_name); // Type name (e.g., (type)node) if let Some(type_name) = &node.type_name { println!(" Type: {}", type_name.as_str()); } // Arguments for (i, arg) in node.arguments.iter().enumerate() { let value_str = match &*arg.literal { Literal::String(s) => format!("\"{}\"", s), Literal::Int(_) => "integer".to_string(), Literal::Decimal(_) => "decimal".to_string(), Literal::Bool(b) => b.to_string(), Literal::Null => "null".to_string(), }; println!(" Arg {}: {}", i, value_str); } // Properties for (name, value) in &node.properties { println!(" Property {}: {:?}", &**name, &*value.literal); } // Children if let Some(children) = &node.children { println!(" Children: {} nodes", children.len()); } } } fn main() -> miette::Result<()> { let doc: Document = knuffel::parse_ast("example.kdl", r###" (database)connection "postgres" host="localhost" port=5432 { pool-size 10 timeout 30 } "###)?; inspect_document(&doc); Ok(()) } ``` -------------------------------- ### Parsing KDL String into Rust Data Structures Source: https://github.com/tailhook/knuffel/blob/main/README.md Example of using `knuffel::parse` function to decode a KDL string into a `Vec`. Requires `knuffel` crate. ```rust # fn main() -> miette::Result<()> { let config = knuffel::parse::>("example.kdl", r#" route "/api" { route "/api/v1" } plugin "http" url="https://example.org/http" "#)?; # Ok(()) # } ``` -------------------------------- ### Flatten Attribute for Composition Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Example of using the `flatten` attribute to include fields from another struct. The target structure must have optional fields and implement `Default`. This example flattens child nodes. ```rust # #[derive(knuffel::Decode, Default)] # struct Common { #[knuffel(child, unwrap(argument))] name: Option, #[knuffel(child, unwrap(argument))] description: Option } #[derive(knuffel::Decode)] struct Plugin { #[knuffel(flatten(child))] common: Common, #[knuffel(child, unwrap(argument))] url: String, } ``` -------------------------------- ### Handle Parse Errors with Miette Source: https://context7.com/tailhook/knuffel/llms.txt Integrates with miette for rich error diagnostics, including source code snippets and precise error locations. This example shows how to catch and report parsing errors. ```rust use miette::{IntoDiagnostic, Result}; use std::fs; #[derive(knuffel::Decode, Debug)] struct Config { #[knuffel(argument)] name: String, #[knuffel(property)] port: u16, } fn parse_config(path: &str) -> Result> { let text = fs::read_to_string(path) .into_diagnostic() .map_err(|e| miette::miette!("cannot read {:?}: {}", path, e))?; Ok(knuffel::parse(path, &text)?) } fn main() -> Result<()> { // Example of handling parse errors with nice output let kdl_with_error = r###" server "web" port=8080 server "api" "###; match knuffel::parse::>("config.kdl", kdl_with_error) { Ok(config) => println!("Parsed {} servers", config.len()), Err(e) => { // Print the error with source code context println!("{:?}", miette::Report::new(e)); // Output includes: // error parsing KDL // × property `port` is required // ╭─[config.kdl:3:9] // 2 │ server "web" port=8080 // 3 │ server "api" // · ────┬─ // · ╰── node starts here // ╰──── } } Ok(()) } ``` -------------------------------- ### Define enum variants for node types Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Example KDL input and corresponding Rust enum definition for handling multiple node types. ```kdl create "xxx" print-string "yyy" line=2 finish ``` ```rust # #[derive(knuffel::Decode)] struct PrintString {} #[derive(knuffel::Decode)] enum Action { Create(#[knuffel(argument)] String), PrintString(PrintString), Finish, #[knuffel(skip)] InternalAction, } ``` -------------------------------- ### Invalid Root Document Structure Example Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Illustrates a struct that cannot be used as a root document because it contains a `property` field. Root structures should only contain `child`, `children`, or unmarked fields. ```rust # #[derive(knuffel::Decode)] # struct NamedNode { #[knuffel(argument)] name: u32 } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] version: u32, #[knuffel(children(name="plugin"))] plugins: Vec, #[knuffel(children(name="datum"))] data: Vec, } ``` -------------------------------- ### Track Source Positions with Span Source: https://context7.com/tailhook/knuffel/llms.txt Capture source position information using `knuffel::span::Span` for runtime error reporting or debugging. This example shows how to attach spans to decoded structs. ```rust use knuffel::span::Span; #[derive(knuffel::Decode, Debug)] #[knuffel(span_type=Span)] struct ConfigItem { #[knuffel(span)] span: Span, #[knuffel(argument)] value: String, } fn main() -> miette::Result<()> { let items: Vec = knuffel::parse("items.kdl", r###" item "first" item "second" "###)?; for item in &items { println!("Item '{}' at byte offset {:?}", item.value, item.span); } Ok(()) } ``` -------------------------------- ### Derive Decode for Document Structure Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Example of deriving knuffel::Decode for a struct with nested vectors of children. The `unwrap(children(name="plugin"))` specifies how to group child nodes. ```rust #[derive(knuffel::Decode)] struct File {} #[derive(knuffel::Decode)] struct Document { #[knuffel(child, unwrap(children(name="plugin")))] plugins: Vec, #[knuffel(child, unwrap(children(name="file")))] files: Vec, } ``` -------------------------------- ### KDL Structure with Annotations Source: https://github.com/tailhook/knuffel/blob/main/README.md An explanation of KDL syntax elements like node names, arguments, properties, and type names, using annotations. ```text foo 1 "three" key="val" { ─┬─ ┬ ───┬─── ────┬──── │ │ │ ╰───── property (can be multiple) │ │ │ │ ╰────┴────────────── arguments │ └── node name bar (role)baz 1 2 ──┬─ └────── type name for node named "baz" } ``` -------------------------------- ### Decode Optional Properties with Full Path Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Demonstrates an alternative syntax for optional properties using the full path to `Option`. This syntax is required due to limitations in Rust's procedural macros. ```rust use std::option::Option as Opt; #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] name: ::std::option::Option, #[knuffel(property)] enabled: Opt, } ``` -------------------------------- ### Manual Error Handling with Miette Report Source: https://github.com/tailhook/knuffel/blob/main/README.md Demonstrates manually converting a `knuffel::parse` error into a `miette::Report` and printing it to stderr, then exiting. ```rust # #[derive(knuffel::Decode, Debug)] # struct Config {} # let file_name = "1.kdl"; # let text = ""; let config = match knuffel::parse::(file_name, text) { Ok(config) => config, Err(e) => { println!("{:?}", miette::Report::new(e)); std::process::exit(1); } }; ``` -------------------------------- ### Capture KDL Properties Source: https://context7.com/tailhook/knuffel/llms.txt Use #[knuffel(property)] to map KDL attributes to struct fields. Use #[knuffel(properties)] to capture remaining attributes into a BTreeMap. ```rust use std::collections::BTreeMap; #[derive(knuffel::Decode, Debug)] struct HttpRequest { #[knuffel(property)] method: String, #[knuffel(property)] url: String, #[knuffel(property(name="content-type"))] // Custom property name content_type: Option, #[knuffel(property, default = 30)] timeout_seconds: u32, #[knuffel(properties)] headers: BTreeMap, // Captures remaining properties } fn main() -> miette::Result<()> { let requests: Vec = knuffel::parse("requests.kdl", r#" request method="POST" url="/api/users" content-type="application/json" \ authorization="Bearer token" x-request-id="abc123" request method="GET" url="/health" timeout-seconds=5 "#)?; assert_eq!(requests[0].method, "POST"); assert_eq!(requests[0].content_type, Some("application/json".into())); assert_eq!(requests[0].headers.get("authorization"), Some(&"Bearer token".to_string())); assert_eq!(requests[1].timeout_seconds, 5); assert_eq!(requests[1].content_type, None); Ok(()) } ``` -------------------------------- ### Enabling Fancy Errors with Miette Source: https://github.com/tailhook/knuffel/blob/main/README.md Configuration for `Cargo.toml` to enable the 'fancy' feature for miette, which provides enhanced error reporting for the KDL parser. ```toml [dependencies] miette = { version="4.3.0", features=["fancy"] } ``` -------------------------------- ### Group Nodes Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Grouping scattered nodes into structured collections. ```kdl plugin "a" file "aa" plugin "b" file "bb" ``` ```kdl plugins { plugin "a" plugin "b" } files { file "aa" file "bb" } ``` ```rust #[derive(knuffel::Decode)] struct Plugin {} ``` -------------------------------- ### Parsing Configuration with Miette Result Source: https://github.com/tailhook/knuffel/blob/main/README.md A helper function `parse_config` that reads a file, parses it using `knuffel::parse`, and returns a `miette::Result`. This simplifies error handling in the main function. ```rust # use std::fs; # #[derive(knuffel::Decode)] # struct Config {} use miette::{IntoDiagnostic, Context}; fn parse_config(path: &str) -> miette::Result { let text = fs::read_to_string(path).into_diagnostic() .wrap_err_with(|| format!("cannot read {:?}", path))?; Ok(knuffel::parse(path, &text)?) } fn main() -> miette::Result<()> { let config = parse_config("my.kdl")?; # Ok(()) } ``` -------------------------------- ### Decode KDL Nodes with Derive Macro Source: https://context7.com/tailhook/knuffel/llms.txt Use the Decode derive macro to map KDL nodes to structs and enums. Supports arguments, properties, children, and default values. ```rust use std::collections::HashMap; // Basic struct with argument and property #[derive(knuffel::Decode, Debug, PartialEq)] struct Server { #[knuffel(argument)] name: String, #[knuffel(property)] port: u16, #[knuffel(property, default = 1)] workers: u32, } // Struct with multiple arguments #[derive(knuffel::Decode, Debug)] struct Point { #[knuffel(arguments)] coords: Vec, } // Struct with variable properties #[derive(knuffel::Decode, Debug)] struct Scores { #[knuffel(properties)] values: HashMap, } // Struct with children #[derive(knuffel::Decode, Debug)] struct Database { #[knuffel(child, unwrap(argument))] host: String, #[knuffel(child, unwrap(argument))] port: u16, #[knuffel(children(name="table"))] tables: Vec, } #[derive(knuffel::Decode, Debug)] struct Table { #[knuffel(argument)] name: String, } // Enum for variant nodes #[derive(knuffel::Decode, Debug)] enum Action { Create(#[knuffel(argument)] String), Delete(#[knuffel(argument)] String), Update { #[knuffel(argument)] id: u64, #[knuffel(property)] value: String, }, #[knuffel(skip)] Internal, } fn main() -> miette::Result<()> { // Parse server config let servers: Vec = knuffel::parse("servers.kdl", r#" server "web" port=8080 server "api" port=3000 workers=4 "#)?; assert_eq!(servers[0].name, "web"); assert_eq!(servers[0].port, 8080); assert_eq!(servers[0].workers, 1); // default value // Parse point with variable arguments let points: Vec = knuffel::parse("points.kdl", r#" point 1.0 2.0 3.0 "#)?; assert_eq!(points[0].coords, vec![1.0, 2.0, 3.0]); // Parse scores with variable properties let scores: Vec = knuffel::parse("scores.kdl", r#" scores alice=100 bob=85 charlie=92 "#)?; assert_eq!(scores[0].values.get("alice"), Some(&100)); // Parse database with children let db: Database = knuffel::parse("db.kdl", r#" host "localhost" port 5432 table "users" table "posts" "#)?; assert_eq!(db.host, "localhost"); assert_eq!(db.tables.len(), 2); // Parse actions enum let actions: Vec = knuffel::parse("actions.kdl", r#" create "user" delete "temp" update 42 value="new" "#)?; println!("{:?}", actions); Ok(()) } ``` -------------------------------- ### Decode Basic Properties and HashMap Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Defines a struct with mandatory string and boolean properties, and a HashMap for properties. Requires `std::collections::HashMap`. ```rust use std::collections::HashMap; #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] name: String, #[knuffel(property)] enabled: bool, #[knuffel(properties)] numbers: HashMap, } ``` -------------------------------- ### Rust Structs for KDL Decoding with Derive Source: https://github.com/tailhook/knuffel/blob/main/README.md Defines Rust structs and enums using `knuffel::Decode` derive macro to map KDL nodes to Rust data structures. Use for parsing nested routes and plugins. ```rust #[derive(knuffel::Decode)] enum TopLevelNode { Route(Route), Plugin(Plugin), } #[derive(knuffel::Decode)] struct Route { #[knuffel(argument)] path: String, #[knuffel(children(name="route"))] subroutes: Vec, } #[derive(knuffel::Decode)] struct Plugin { #[knuffel(argument)] name: String, #[knuffel(property)] url: String, } ``` -------------------------------- ### Rename Property in KDL Source Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Illustrates how to explicitly rename a property in the KDL source using the `name=` attribute within `knuffel(property(...))`. The KDL property `pluginName` maps to the Rust field `name`. ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property(name="pluginName"))] name: String, } ``` -------------------------------- ### knuffel::parse_ast Source: https://context7.com/tailhook/knuffel/llms.txt Parses KDL text into an abstract syntax tree (AST) without decoding into specific Rust types. ```APIDOC ## knuffel::parse_ast ### Description Parses KDL text into an abstract syntax tree. This is useful for inspecting the raw document structure or implementing custom parsing logic. ### Parameters - **filename** (String) - Required - The name of the file being parsed. - **kdl_text** (String) - Required - The KDL document content as a string. ### Response - **Document** - The resulting AST structure containing nodes, arguments, properties, and children. ``` -------------------------------- ### Parse Children Nodes into Rust Structures Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Using #[knuffel(child)] and #[knuffel(children)] to map KDL nodes to Rust structs and enums. ```kdl node { version 1 plugin "xxx" datum "yyy" } ``` ```rust #[derive(knuffel::Decode)] enum Setting { Plugin(#[knuffel(argument)] String), Datum(#[knuffel(argument)] String), } #[derive(knuffel::Decode)] struct Version { #[knuffel(argument)] number: u32 } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(child)] version: Version, #[knuffel(children)] settings: Vec } ``` -------------------------------- ### Capture Positional Arguments with Field Attributes Source: https://context7.com/tailhook/knuffel/llms.txt Use argument and arguments attributes to capture positional values from KDL nodes. Optional arguments are supported via Option. ```rust #[derive(knuffel::Decode, Debug, PartialEq)] struct Command { #[knuffel(argument)] name: String, #[knuffel(argument)] target: Option, // Optional arguments use Option #[knuffel(arguments)] flags: Vec, // Captures remaining arguments } fn main() -> miette::Result<()> { let cmds: Vec = knuffel::parse("commands.kdl", r#" command "build" command "run" "main.rs" command "test" "lib.rs" "--verbose" "--coverage" "#)?; assert_eq!(cmds[0], Command { name: "build".into(), target: None, flags: vec![] }); assert_eq!(cmds[1], Command { name: "run".into(), target: Some("main.rs".into()), flags: vec![] }); assert_eq!(cmds[2].flags, vec!["--verbose", "--coverage"]); Ok(()) } ``` -------------------------------- ### Convert Properties to Children Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Using unwrap(argument) to parse child nodes as if they were properties. ```kdl plugin name="my-plugin" url="https://example.com" {} ``` ```kdl plugin { name "my-plugin" url "https://example.com" } ``` ```rust #[derive(knuffel::Decode)] struct Plugin { #[knuffel(child, unwrap(argument))] name: String, #[knuffel(child, unwrap(argument))] url: String, } ``` -------------------------------- ### knuffel::parse_with_context Source: https://context7.com/tailhook/knuffel/llms.txt Parses KDL text with additional context data accessible during the decoding process. ```APIDOC ## knuffel::parse_with_context ### Description Parses KDL text while allowing the injection of custom context data that can be accessed by custom decoders. ### Parameters - **filename** (String) - Required - The name of the file being parsed. - **kdl_text** (String) - Required - The KDL document content as a string. - **context_fn** (Closure) - Required - A closure that receives a mutable reference to the Context to set custom state. ``` -------------------------------- ### Parse KDL with Custom Context using knuffel::parse_with_context Source: https://context7.com/tailhook/knuffel/llms.txt This function parses KDL text while providing additional context data accessible during decoding. It's ideal for passing configuration or state to custom decoders. The context is set using a closure that mutates a `Context` object. ```rust use knuffel::decode::Context; use knuffel::span::Span; #[derive(knuffel::Decode, Debug)] struct Config { #[knuffel(child, unwrap(argument))] name: String, } fn main() -> miette::Result<()> { let kdl_text = r"name "my-app"" // Custom context type struct AppInfo(String); let config: Config = knuffel::parse_with_context("config.kdl", kdl_text, |ctx: &mut Context| { ctx.set(AppInfo("production".to_string())); })?; println!("Config name: {}", config.name); Ok(()) } ``` -------------------------------- ### Field Attributes: #[knuffel(argument)] and #[knuffel(arguments)] Source: https://context7.com/tailhook/knuffel/llms.txt Annotate fields to capture positional arguments from KDL nodes. Single `argument` captures one value, `arguments` captures remaining values into a collection. ```APIDOC ## Field Attributes: #[knuffel(argument)] and #[knuffel(arguments)] ### Description Annotate fields to capture positional arguments from KDL nodes. Single `argument` captures one value, `arguments` captures remaining values into a collection. ### Method N/A (This is a derive macro attribute, not an API endpoint) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ## Examples ### Command struct with arguments and arguments ```rust #[derive(knuffel::Decode, Debug, PartialEq)] struct Command { #[knuffel(argument)] name: String, #[knuffel(argument)] target: Option, // Optional arguments use Option #[knuffel(arguments)] flags: Vec, // Captures remaining arguments } ``` ``` -------------------------------- ### Parse KDL into AST with knuffel::parse_ast Source: https://context7.com/tailhook/knuffel/llms.txt This function parses KDL text into an abstract syntax tree (AST) without decoding into Rust types. It's useful for inspecting the raw document structure or implementing custom parsing logic. The AST nodes contain span information for precise location tracking. ```rust use knuffel::span::Span; use knuffel::ast::{Document, Literal}; fn main() -> miette::Result<()> { let kdl_text = r" node "arg1" 42 key="value" { child "nested" } "; let doc: Document = knuffel::parse_ast("example.kdl", kdl_text)?; for node in &doc.nodes { println!("Node name: {}", &*node.node_name); for arg in &node.arguments { match &*arg.literal { Literal::String(s) => println!(" String arg: {}", s), Literal::Int(i) => println!(" Int arg"), _ => println!(" Other arg"), } } for (name, value) in &node.properties { println!(" Property: {} = {:?}", &**name, &*value.literal); } if let Some(children) = &node.children { println!(" Children count: {}", children.len()); } } Ok(()) } ``` -------------------------------- ### Decode Node Arguments with Knuffel Derive Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use `#[knuffel(argument)]` for single positional arguments and `#[knuffel(arguments)]` for sequences. Fields marked with `arguments` can only appear once and must not be followed by `argument`. ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(argument)] first: String, #[knuffel(argument)] second: bool, #[knuffel(arguments)] numbers: Vec, } ``` ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(argument)] first: Option, #[knuffel(argument)] second: Option, } ``` -------------------------------- ### #[derive(knuffel::Decode)] - Decode Nodes Source: https://context7.com/tailhook/knuffel/llms.txt The primary derive macro for decoding KDL nodes into Rust structs and enums. It supports arguments, properties, and children with various annotations. ```APIDOC ## #[derive(knuffel::Decode)] - Decode Nodes ### Description The primary derive macro for decoding KDL nodes into Rust structs and enums. Supports arguments, properties, and children with various annotations. ### Method N/A (This is a derive macro, not an API endpoint) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ## Examples ### Basic struct with argument and property ```rust #[derive(knuffel::Decode, Debug, PartialEq)] struct Server { #[knuffel(argument)] name: String, #[knuffel(property)] port: u16, #[knuffel(property, default = 1)] workers: u32, } ``` ### Struct with multiple arguments ```rust #[derive(knuffel::Decode, Debug)] struct Point { #[knuffel(arguments)] coords: Vec, } ``` ### Struct with variable properties ```rust #[derive(knuffel::Decode, Debug)] struct Scores { #[knuffel(properties)] values: HashMap, } ``` ### Struct with children ```rust #[derive(knuffel::Decode, Debug)] struct Database { #[knuffel(child, unwrap(argument))] host: String, #[knuffel(child, unwrap(argument))] port: u16, #[knuffel(children(name="table"))] tables: Vec
, } #[derive(knuffel::Decode, Debug)] struct Table { #[knuffel(argument)] name: String, } ``` ### Enum for variant nodes ```rust #[derive(knuffel::Decode, Debug)] enum Action { Create(#[knuffel(argument)] String), Delete(#[knuffel(argument)] String), Update { #[knuffel(argument)] id: u64, #[knuffel(property)] value: String, }, #[knuffel(skip)] Internal, } ``` ``` -------------------------------- ### Parse Bytes Argument as UTF-8 String Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Decodes a byte array argument directly as a UTF-8 encoded string. This is an alternative to Base64 encoding when the data is text. The `bytes` marker is used. ```kdl response "Hello world!" ``` -------------------------------- ### Decode Node Properties with Knuffel Derive Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use `#[knuffel(property)]` for single named properties and `#[knuffel(properties)]` for sequences. Properties are parsed regardless of order, and later values override earlier ones if duplicated. Fields marked with `properties` can only appear once and must not be followed by `property`. ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] name: String, #[knuffel(property)] enabled: bool, #[knuffel(properties)] values: Vec, } ``` -------------------------------- ### Default Attribute - Value Form Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Demonstrates the `default=value` form of the `default` attribute, where a specific Rust expression is used to provide a default value if the field is not present in the input. ```rust # #[derive(knuffel::Decode)] # struct MyNode { #[knuffel(property)] name: String } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property, default="unnamed".into())] name: String, } ``` -------------------------------- ### Rust Struct for Root Document Decoding Source: https://github.com/tailhook/knuffel/blob/main/README.md Defines a Rust struct `Document` for parsing a KDL file where a specific node serves as the root. Uses `knuffel::Decode` for version, routes, and plugins. ```rust #[derive(knuffel::Decode)] struct Document { #[knuffel(child, unwrap(argument))] version: Option, #[knuffel(children(name="route"))] routes: Vec, #[knuffel(children(name="plugin"))] plugins: Vec, } let config = parse::("example.kdl", r#" version "2.0" route "/api" { route "/api/v1" } plugin "http" url="https://example.org/http" "#)?; ``` -------------------------------- ### Decode Property with Kebab-Case Naming Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Shows how Knuffel automatically converts snake_case field names to kebab-case in KDL by default. The KDL property `plugin-name` maps to the Rust field `plugin_name`. ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] plugin_name: String, } ``` ```kdl node plugin-name="my_plugin" ``` -------------------------------- ### Decode Optional Properties Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Defines a struct with optional string and boolean properties. These properties may be absent or explicitly set to null in the KDL source. ```rust #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property)] name: Option, #[knuffel(property)] enabled: Option, } ``` ```kdl node name=null ``` -------------------------------- ### Parse Bytes Argument with Base64 Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Decodes a byte array argument using Base64 encoding. This requires the `base64` feature to be enabled. The `bytes` marker is used. ```rust #[derive(knuffel::Decode)] struct Response { #[knuffel(argument, bytes)] body: Vec, } ``` ```kdl response (base64)"SGVsbG8gd29ybGQh" ``` -------------------------------- ### Default Attribute - Marker Form Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Shows the marker form of the `default` attribute. If a property is not encountered during parsing, the field will be initialized using its `std::default::Default` implementation. ```rust # #[derive(knuffel::Decode)] # struct MyNode { #[knuffel(property)] first: String } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property, default)] first: String, } ``` -------------------------------- ### Capture KDL Child Nodes Source: https://context7.com/tailhook/knuffel/llms.txt Use #[knuffel(child)] or #[knuffel(children)] to map nested KDL nodes to struct fields. Use the name attribute to filter specific node types. ```rust #[derive(knuffel::Decode, Debug)] struct Route { #[knuffel(argument)] path: String, #[knuffel(children(name="route"))] subroutes: Vec, } #[derive(knuffel::Decode, Debug)] struct Router { #[knuffel(child)] default_handler: Option, #[knuffel(children(name="route"))] routes: Vec, #[knuffel(child)] fallback: bool, // Boolean child - presence/absence } #[derive(knuffel::Decode, Debug)] struct Handler { #[knuffel(argument)] name: String, } fn main() -> miette::Result<()> { let router: Router = knuffel::parse("router.kdl", r#" default-handler "404" route "/api" { route "/users" route "/posts" } route "/static" fallback "#)?; assert_eq!(router.default_handler.as_ref().unwrap().name, "404"); assert_eq!(router.routes.len(), 2); assert_eq!(router.routes[0].path, "/api"); assert_eq!(router.routes[0].subroutes.len(), 2); assert!(router.fallback); Ok(()) } ``` -------------------------------- ### Parse String Properties with FromStr Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Decodes a string property into a type that implements `std::str::FromStr`, such as `std::net::SocketAddr`. Requires the `str` marker. ```rust #[derive(knuffel::Decode)] struct Server { #[knuffel(property, str)] listen: std::net::SocketAddr, } ``` ```kdl server listen="127.0.0.1:8080" ``` -------------------------------- ### Unwrap Child Nodes Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Using the unwrap attribute to simplify document structure by flattening nested nodes. ```rust #[derive(knuffel::Decode)] struct Node { #[knuffel(child, unwrap(/* attributes */))] field: String, } ``` ```rust #[derive(knuffel::Decode)] struct TmpChild { #[knuffel(/* attributes */)] field: String, } #[derive(knuffel::Decode)] struct Node { #[knuffel(child)] field: TmpChild, } ``` -------------------------------- ### Default Attribute for Optional Fields Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Illustrates using `default` with `Option` types. `Some` should be included in the default value for optional fields. Scalar values can be overridden by `null`. ```rust # #[derive(knuffel::Decode)] # struct MyNode { #[knuffel(property)] name: Option } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(property, default=Some("unnamed".into()))] name: Option, } ``` -------------------------------- ### Store node names in a struct Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use the #[knuffel(node_name)] attribute to capture the name of the node itself. ```rust #[derive(knuffel::Decode)] struct Node { #[knuffel(node_name)] node_name: String, } ``` -------------------------------- ### Validate node type names with FromStr Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Implement FromStr to validate or transform the type name string into a custom enum. ```rust pub enum PluginType { Builtin, External, } impl std::str::FromStr for PluginType { type Err = Box; fn from_str(s: &str) -> Result { match s { "builtin" => Ok(PluginType::Builtin), "external" => Ok(PluginType::External), _ => Err("Plugin type name must be `builtin` or `external`")?, } } } #[derive(knuffel::Decode)] struct Node { #[knuffel(type_name)] type_name: PluginType, } ``` -------------------------------- ### Capture node spans Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use the #[knuffel(span)] attribute and specify the span_type to track source code positions. ```rust use knuffel::span::Span; // or LineSpan #[derive(knuffel::Decode)] #[knuffel(span_type=Span)] struct Node { #[knuffel(span)] span: Span, // This can be user type decoded from Span } ``` -------------------------------- ### knuffel::parse Source: https://context7.com/tailhook/knuffel/llms.txt Parses KDL text directly into Rust types that implement the Decode or DecodeChildren trait. ```APIDOC ## knuffel::parse ### Description The primary function for parsing KDL text directly into Rust types. It returns detailed error information via miette diagnostics. ### Parameters - **filename** (String) - Required - The name of the file being parsed (used for error reporting). - **kdl_text** (String) - Required - The KDL document content as a string. ### Request Example let config: Config = knuffel::parse("config.kdl", kdl_text)?; ``` -------------------------------- ### Filter Children by Name Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Using children(name="...") to group specific node types into separate fields. ```rust #[derive(knuffel::Decode)] struct NamedNode { #[knuffel(argument)] name: u32 } #[derive(knuffel::Decode)] struct MyNode { #[knuffel(children(name="plugin"))] plugins: Vec, #[knuffel(children(name="datum"))] data: Vec, } ``` -------------------------------- ### #[derive(knuffel::DecodeScalar)] - Decode Scalar Values Source: https://context7.com/tailhook/knuffel/llms.txt Derive macro for decoding KDL scalar values into Rust enums. Converts enum variants to kebab-case for matching. ```APIDOC ## #[derive(knuffel::DecodeScalar)] - Decode Scalar Values ### Description Derive macro for decoding KDL scalar values into Rust enums. Converts enum variants to kebab-case for matching. ### Method N/A (This is a derive macro, not an API endpoint) ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ## Examples ### LogLevel Enum ```rust #[derive(knuffel::DecodeScalar, Debug, PartialEq)] enum LogLevel { Debug, Info, Warning, Error, CriticalError, // matches "critical-error" in KDL } ``` ### Environment Enum ```rust #[derive(knuffel::DecodeScalar, Debug, PartialEq)] enum Environment { Development, Staging, Production, } ``` ### AppConfig Struct using Enums ```rust #[derive(knuffel::Decode, Debug)] struct AppConfig { #[knuffel(argument)] env: Environment, #[knuffel(property)] log_level: LogLevel, } ``` ``` -------------------------------- ### Parse KDL into Rust Types with knuffel::parse Source: https://context7.com/tailhook/knuffel/llms.txt Use this function to parse KDL text directly into Rust types that implement the `Decode` or `DecodeChildren` trait. It returns detailed error information via miette diagnostics. Ensure necessary derive macros are used on your structs. ```rust use miette::IntoDiagnostic; #[derive(knuffel::Decode, Debug)] struct Plugin { #[knuffel(argument)] name: String, #[knuffel(property)] url: String, #[knuffel(child, unwrap(argument))] version: String, } #[derive(knuffel::Decode, Debug)] struct Config { #[knuffel(child, unwrap(argument))] version: String, #[knuffel(children(name="plugin"))] plugins: Vec, } fn main() -> miette::Result<()> { let kdl_text = r#" version "2.0" plugin "auth" url="https://example.org/auth" { version "1.0.0" } plugin "logger" url="https://example.org/logger" { version "2.1.0" } "#; let config: Config = knuffel::parse("config.kdl", kdl_text)?; println!("Config version: {}", config.version); for plugin in &config.plugins { println!("Plugin: {} at {} (v{})", plugin.name, plugin.url, plugin.version); } // Output: // Config version: 2.0 // Plugin: auth at https://example.org/auth (v1.0.0) // Plugin: logger at https://example.org/logger (v2.1.0) Ok(()) } ``` -------------------------------- ### Flatten Structures Source: https://context7.com/tailhook/knuffel/llms.txt Use #[knuffel(flatten)] to merge fields from a common struct into another struct definition. ```rust #[derive(knuffel::Decode, Debug, Default)] struct CommonFields { #[knuffel(child, unwrap(argument))] name: Option, #[knuffel(child, unwrap(argument))] description: Option, } #[derive(knuffel::Decode, Debug)] struct Plugin { #[knuffel(flatten(child))] common: CommonFields, #[knuffel(child, unwrap(argument))] url: String, } #[derive(knuffel::Decode, Debug)] struct Theme { #[knuffel(flatten(child))] common: CommonFields, #[knuffel(child, unwrap(argument))] primary_color: String, } fn main() -> miette::Result<()> { let plugins: Vec = knuffel::parse("plugins.kdl", r#" plugin { name "auth-plugin" description "Handles authentication" url "https://example.org/auth" } "#)?; assert_eq!(plugins[0].common.name, Some("auth-plugin".into())); assert_eq!(plugins[0].url, "https://example.org/auth"); Ok(()) } ``` -------------------------------- ### Store node type names in a struct Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use the #[knuffel(type_name)] attribute to capture the type name of a node. ```rust #[derive(knuffel::Decode)] struct Node { #[knuffel(type_name)] type_name: String, } ``` -------------------------------- ### Define a scalar enum with DecodeScalar Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode_scalar.md Use the DecodeScalar derive on enums that contain no data to enable scalar decoding. ```rust #[derive(knuffel::DecodeScalar)] enum Color { Red, Blue, Green, InfraRed, } ``` -------------------------------- ### Parse Boolean Child Fields Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Tracking the presence of a child node using a boolean field. ```kdl plugin "first" { auto-start } plugin "second" ``` ```rust #[derive(knuffel::Decode)] struct Plugin { #[knuffel(child)] auto_start: bool, } ``` -------------------------------- ### Specify span type for decoding Source: https://github.com/tailhook/knuffel/blob/main/derive/derive_decode.md Use span_type to restrict the decoder to a specific span implementation. ```rust use knuffel::span::Span; // or LineSpan #[derive(knuffel::Decode)] #[knuffel(span_type=Span)] struct MyStruct { #[knuffel(span)] span: Span, } ```