uppsala 0.4.0

A pure Rust XML parser, DOM, namespace, XPath, and XSD validation library
Documentation
//! Build XML programmatically using both XmlWriter and DOM construction.
//!
//! Run with: `cargo run --example build_xml`

use uppsala::dom::{NodeKind, QName};
use uppsala::{parse, XmlWriteOptions, XmlWriter};

fn main() {
    // ── Part 1: XmlWriter (streaming construction) ──
    println!("=== Part 1: XmlWriter ===\n");

    let mut w = XmlWriter::new();
    w.write_declaration();
    w.start_element(
        "feed",
        &[("xmlns", "http://www.w3.org/2005/Atom"), ("xml:lang", "en")],
    );

    w.start_element("title", &[]);
    w.text("My Blog");
    w.end_element("title");

    w.start_element("link", &[("href", "https://example.com/")]);
    w.end_element("link");

    // First entry
    w.start_element("entry", &[]);
    w.start_element("title", &[]);
    w.text("Hello World");
    w.end_element("title");
    w.start_element("content", &[("type", "text")]);
    w.text("This is my first post. Special chars: <, >, & are escaped automatically.");
    w.end_element("content");
    w.end_element("entry");

    // Second entry with CDATA
    w.start_element("entry", &[]);
    w.start_element("title", &[]);
    w.text("Code Example");
    w.end_element("title");
    w.start_element("content", &[("type", "html")]);
    w.cdata("<p>This content is in a <b>CDATA</b> section.</p>");
    w.end_element("content");
    w.end_element("entry");

    w.end_element("feed");

    let xml = w.into_string();
    println!("Generated Atom feed ({} bytes):\n", xml.len());
    println!("{}\n", xml);

    // Parse it back to verify it's well-formed
    let doc = parse(&xml).expect("Generated XML should be well-formed");
    let entries = doc.get_elements_by_tag_name("entry");
    println!("Verified: parsed back {} entries\n", entries.len());

    // ── Part 2: DOM Construction ──
    println!("=== Part 2: DOM Construction ===\n");

    let mut doc = uppsala::dom::Document::new();
    let root = doc.root();

    // Create elements and build a tree
    let html = doc.create_element(QName::local("html"));
    doc.append_child(root, html);

    let head = doc.create_element(QName::local("head"));
    doc.append_child(html, head);

    let title = doc.create_element(QName::local("title"));
    doc.append_child(head, title);
    let title_text = doc.create_text("Uppsala Example");
    doc.append_child(title, title_text);

    let body = doc.create_element(QName::local("body"));
    doc.append_child(html, body);

    let h1 = doc.create_element(QName::local("h1"));
    doc.append_child(body, h1);
    let h1_text = doc.create_text("Hello from Uppsala");
    doc.append_child(h1, h1_text);

    let p = doc.create_element(QName::local("p"));
    doc.append_child(body, p);
    let p_text = doc.create_text("This document was built programmatically.");
    doc.append_child(p, p_text);

    // Add a comment
    let comment = doc.create_comment("Generated by Uppsala");
    doc.insert_before(html, comment, body);

    // Compact output
    println!("Compact:\n{}\n", doc.to_xml());

    // Pretty-printed output
    let opts = XmlWriteOptions::pretty("  ");
    println!("Pretty:\n{}\n", doc.to_xml_with_options(&opts));

    // ── Part 3: DOM Mutation ──
    println!("=== Part 3: DOM Mutation ===\n");

    let xml = r#"<ul><li>First</li><li>Second</li><li>Third</li></ul>"#;
    let mut doc = parse(xml).expect("Failed to parse");

    println!("Before: {}", doc.to_xml());

    // Find all <li> elements
    let items = doc.get_elements_by_tag_name("li");
    let ul = doc.document_element().unwrap();

    // Add a new item at the end
    let new_li = doc.create_element(QName::local("li"));
    let new_text = doc.create_text("Fourth");
    doc.append_child(new_li, new_text);
    doc.append_child(ul, new_li);

    // Insert an item before "Second"
    let inserted_li = doc.create_element(QName::local("li"));
    let inserted_text = doc.create_text("Inserted");
    doc.append_child(inserted_li, inserted_text);
    doc.insert_before(ul, inserted_li, items[1]);

    // Remove "Third"
    doc.remove_child(ul, items[2]);

    println!("After:  {}", doc.to_xml());

    // Show the final list
    println!("\nFinal items:");
    for child_id in doc.children(ul) {
        if let Some(NodeKind::Element(_)) = doc.node_kind(child_id) {
            println!("  - {}", doc.text_content_deep(child_id));
        }
    }
}