# 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 `&`.
## 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).