xdoc-rs 0.1.1

Declarative XML engine for Rust
Documentation
# Feature: Macro `xml!`

`xml!` permite escribir XML con sintaxis de tags dentro de Rust. Es la forma mas
ergonomica para documentos declarativos, componentes y namespaces.

La macro no parsea XML en runtime. En compile time traduce tags a llamadas del
builder y de component.

## Que aprenderas aqui

[] Crear documentos con tags.
[] Interpolar expresiones Rust.
[] Pasar atributos dinamicos.
[] Usar namespaces.
[] Usar componentes, props, children y listas.
[] Entender los limites actuales de la macro.

## Archivos

[] `src/macros/mod.rs`
[] `crates/xdoc-macros/src/lib.rs`
[] `crates/xdoc-macros/src/ast.rs`
[] `crates/xdoc-macros/src/parser.rs`
[] `crates/xdoc-macros/src/codegen.rs`

## Primer documento

```rust
use xdoc::core::XmlResult;
use xdoc::macros::xml;

fn build() -> XmlResult<xdoc::core::Document> {
    xml! {
        <Document>
            <ID>DOC-001</ID>
            <Title>Hello XML</Title>
        </Document>
    }?
    .into_document()
}
```

## Interpolacion de texto

Usa `{ expr }` para insertar valores Rust como texto.

```rust
let id = "DOC-001";
let title = String::from("Dynamic title");

let document = xml! {
    <Document>
        <ID>{ id }</ID>
        <Title>{ title }</Title>
    </Document>
}?
.into_document()?;
```

El texto se escapa cuando se serializa. Si `title` contiene `&`, se escribe
como `&amp;`.

## Atributos dinamicos

```rust
let code = "A001";
let quantity = 3;

let item = xml! {
    <Item code={ code } quantity={ quantity }>
        Keyboard
    </Item>
}?;
```

Los atributos dinamicos usan `ToString`.

## Namespaces

Declara namespaces con `xmlns` o `xmlns:prefix`.

```rust
let document = xml! {
    <doc:Document
        xmlns="urn:example:default"
        xmlns:doc="urn:example:document"
        xmlns:meta="urn:example:metadata"
        meta:version="1"
    >
        <doc:Title>Namespaced document</doc:Title>
    </doc:Document>
}?
.into_document()?;
```

La macro valida que los prefijos usados hayan sido declarados en el scope. Esto
ayuda a detectar errores antes de ejecutar el programa.

## Comentarios

```rust
let document = xml! {
    <Root>
        <!-- generated by xdoc -->
        <Value>ok</Value>
    </Root>
}?
.into_document()?;
```

## Componentes

Los componentes se invocan con `<{Component}>`.

```rust
use xdoc::builder::ElementBuilder;
use xdoc::core::XmlResult;
use xdoc::macros::xml;

#[derive(Clone)]
struct HeaderProps {
    title: &'static str,
}

#[allow(non_snake_case)]
fn Header(props: HeaderProps) -> XmlResult<ElementBuilder> {
    xml! { <Header title={ props.title }/> }?
        .into_fragment()
        .into_single_element()
}

let document = xml! {
    <Document>
        <{Header} title="Main"/>
    </Document>
}?
.into_document()?;
```

## Componentes con children

```rust
use xdoc::builder::ElementBuilder;
use xdoc::component::Children;
use xdoc::core::XmlResult;
use xdoc::macros::xml;

#[derive(Clone)]
struct SectionProps {
    name: &'static str,
}

#[allow(non_snake_case)]
fn Section(props: SectionProps, children: Children) -> XmlResult<ElementBuilder> {
    xml! {
        <Section name={ props.name }>
            { children }
        </Section>
    }?
    .into_fragment()
    .into_single_element()
}

let document = xml! {
    <Document>
        <{Section} name="items">
            <Item>A</Item>
            <Item>B</Item>
        </{Section}>
    </Document>
}?
.into_document()?;
```

## Iterar vectores

`xml!` no tiene una sintaxis especial tipo `for`. Usas Rust normal para crear
un vector de templates/fragments y luego lo interpolas.

```rust
struct Line {
    code: &'static str,
    description: &'static str,
}

let lines = vec![
    Line { code: "A", description: "Alpha" },
    Line { code: "B", description: "Beta" },
];

let line_nodes = lines
    .iter()
    .map(|line| {
        xml! {
            <Line code={ line.code }>
                <Description>{ line.description }</Description>
            </Line>
        }
    })
    .collect::<XmlResult<Vec<_>>>()?;

let document = xml! {
    <Document>
        <Lines>{ line_nodes }</Lines>
    </Document>
}?
.into_document()?;
```

## Fragments reutilizables

```rust
let header = xml! {
    <Header>
        <ID>DOC-001</ID>
        <IssueDate>2026-06-12</IssueDate>
    </Header>
}?;

let document = xml! {
    <Document>
        { header }
        <Body>...</Body>
    </Document>
}?
.into_document()?;
```

## Convertir la salida

`xml!` retorna `XmlResult<XmlTemplate>`.

```rust
let template = xml! { <Root/> }?;
let fragment = template.clone().into_fragment();
let document = template.into_document()?;
```

Usa:

[] `into_document()` si hay exactamente un root.
[] `into_fragment()` si quieres insertar la salida como children.

## Errores comunes

[] Usar un prefijo sin `xmlns:prefix`.
[] Crear mas de un root y llamar `into_document()`.
[] Esperar que texto literal conserve espacios exactamente como en XML raw; la
   macro sigue tokenizacion Rust para texto simple.
[] Pasar props complejos directamente en el tag; calcula antes y usa un
   identificador.
[] Intentar parsear XML externo con `xml!`; usa `parser` para XML externo.

## Limites actuales

[] Component props deben ser identificadores simples.
[] Namespace declarations deben ser string literals.
[] No es un parser XML runtime.
[] No implementa una sintaxis especial de control de flujo.
[] No tiene runtime reactivo.

## Siguiente lectura

[] [component.md](component.md) para disenar componentes.
[] [builder.md](builder.md) para entender la API que genera la macro.
[] [writer.md](writer.md) para serializar.

[Volver al indice](features.md).