use std::env;
use std::fs;
use std::path::Path;
use xdoc::builder::ElementBuilder;
use xdoc::component::Children;
use xdoc::core::XmlResult;
use xdoc::macros::xml;
use xdoc::writer::{to_string_pretty, WriterConfig};
#[derive(Debug, Clone)]
struct DocumentHeaderProps {
id: String,
issue_date: String,
}
#[allow(non_snake_case)]
fn DocumentHeader(props: DocumentHeaderProps) -> XmlResult<ElementBuilder> {
xml! {
<doc:Header xmlns:doc="urn:xdoc:example:document">
<doc:ID>{ props.id }</doc:ID>
<doc:IssueDate>{ props.issue_date }</doc:IssueDate>
</doc:Header>
}?
.into_fragment()
.into_single_element()
}
#[derive(Debug, Clone)]
struct SectionProps {
name: &'static str,
}
#[allow(non_snake_case)]
fn Section(props: SectionProps, children: Children) -> XmlResult<ElementBuilder> {
xml! {
<doc:Section xmlns:doc="urn:xdoc:example:document" name={ props.name }>
{ children }
</doc:Section>
}?
.into_fragment()
.into_single_element()
}
#[derive(Debug, Clone)]
struct LineItemProps {
code: &'static str,
quantity: u32,
subtotal: &'static str,
}
#[allow(non_snake_case)]
fn LineItem(props: LineItemProps, children: Children) -> XmlResult<ElementBuilder> {
xml! {
<doc:Line
xmlns:doc="urn:xdoc:example:document"
xmlns:meta="urn:xdoc:example:metadata"
meta:code={ props.code }
meta:quantity={ props.quantity }
subtotal={ props.subtotal }
>
{ children }
</doc:Line>
}?
.into_fragment()
.into_single_element()
}
#[derive(Debug, Clone)]
struct LineData {
code: &'static str,
quantity: u32,
subtotal: &'static str,
description: &'static str,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let output_path = env::args()
.nth(1)
.unwrap_or_else(|| "target/xdoc-example.xml".to_owned());
let document_id = "DOC-001";
let issue_date = "2026-06-11";
let customer_name = "Acme Components";
let lines = Vec::from([
LineData {
code: "A001",
quantity: 3,
subtotal: "1250.00",
description: "Generated with xml macro children",
},
LineData {
code: "B210",
quantity: 1,
subtotal: "340.00",
description: "Generated from a vector item",
},
LineData {
code: "C305",
quantity: 5,
subtotal: "875.50",
description: "Preserves vector order",
},
]);
let customer_children = xml! {
<doc:Customer xmlns:doc="urn:xdoc:example:document">{ customer_name }</doc:Customer>
}?;
let lines_children = lines
.iter()
.map(|line| {
xml! {
<{LineItem} code={ line.code } quantity={ line.quantity } subtotal={ line.subtotal }>
<doc:Description xmlns:doc="urn:xdoc:example:document">
{ line.description }
</doc:Description>
</{LineItem}>
}
})
.collect::<XmlResult<Vec<_>>>()?;
let document = xml! {
<doc:Document
xmlns="urn:xdoc:example:default"
xmlns:doc="urn:xdoc:example:document"
xmlns:meta="urn:xdoc:example:metadata"
kind="example"
meta:version="1"
>
<{DocumentHeader} id={ document_id.to_owned() } issue_date={ issue_date.to_owned() }/>
<{Section} name="customer">
{ customer_children }
</{Section}>
<{Section} name="lines">
{ lines_children }
</{Section}>
</doc:Document>
}?
.into_document()?;
let xml = to_string_pretty(
&document,
WriterConfig::pretty().with_xml_declaration(false),
)?;
if let Some(parent) = Path::new(&output_path).parent() {
fs::create_dir_all(parent)?;
}
fs::write(&output_path, xml)?;
println!("wrote {output_path}");
Ok(())
}