oxml 0.0.6

A pure Rust XML toolkit with zero unsafe code: parsing, an ergonomic tree, and XPath
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2026 oxml. All rights reserved.
//
// @generated by scripts/generate-doc-tests.py -- do not edit.
//
// Each test is one ```rust block from a file under doc/. Edit the
// document, then re-run the script. CI regenerates this file and fails
// if the result differs from what is committed.

//! Code blocks from `doc/`, compiled and run.

#![allow(unused_variables, unused_imports, clippy::all, clippy::pedantic)]

/// From `doc/MIGRATION-FROM-LIBXML.md`, line 56.
#[rustfmt::skip]
#[test]
fn migration_from_libxml_md_line_56() {
    use oxml::parse;

    let input = "<a></b>";
    let error = parse(input).unwrap_err();
    let (line, column) = error.line_column(input);
    assert_eq!((line, column), (1, 4));
}

/// From `doc/MIGRATION-FROM-QUICK-XML.md`, line 55.
#[rustfmt::skip]
#[test]
fn migration_from_quick_xml_md_line_55() {
    use oxml::{XPath, parse};

    let xml = "<r><book><title>Dune</title></book><book><title>Germinal</title></book></r>";
    let doc = parse(xml).unwrap();
    let q = XPath::compile("//title").unwrap();
    let titles: Vec<_> = q.evaluate(&doc).nodes().unwrap()
        .iter().map(|&n| doc.text(n)).collect();
    assert_eq!(titles, ["Dune", "Germinal"]);
}

/// From `doc/MIGRATION-FROM-ROXMLTREE.md`, line 51.
#[rustfmt::skip]
#[test]
fn migration_from_roxmltree_md_line_51() {
    use oxml::parse;

    let doc = parse("<r><a/></r>").unwrap();
    let root = doc.root_element().unwrap();
    // roxmltree: root.children().count()
    assert_eq!(doc.children(root).len(), 1);
}

/// From `doc/MIGRATION-FROM-ROXMLTREE.md`, line 64.
#[rustfmt::skip]
#[test]
fn migration_from_roxmltree_md_line_64() {
    use oxml::parse;

    let doc = parse("<p>a<em>b</em>c</p>").unwrap();
    let p = doc.root_element().unwrap();
    // roxmltree's text() would give "a"
    assert_eq!(doc.text(p), "abc");
}

/// From `doc/MIGRATION-FROM-ROXMLTREE.md`, line 82.
#[rustfmt::skip]
#[test]
fn migration_from_roxmltree_md_line_82() {
    use oxml::{XPath, parse};

    let doc = parse(r#"<r><a href="one"/><a href="two"/></r>"#).unwrap();

    // roxmltree:
    //   doc.descendants()
    //      .filter(|n| n.has_tag_name("a"))
    //      .filter_map(|n| n.attribute("href"))
    //      .collect::<Vec<_>>()

    let q = XPath::compile("//a/@href").unwrap();
    let hrefs: Vec<_> = q.evaluate(&doc).nodes().unwrap()
        .iter().map(|&n| doc.text(n)).collect();
    assert_eq!(hrefs, ["one", "two"]);
}

/// From `doc/MIGRATION-FROM-SXD.md`, line 44.
#[rustfmt::skip]
#[test]
fn migration_from_sxd_md_line_44() {
    use oxml::{XPath, parse};

    let doc = parse("<r><a/><a/></r>").unwrap();

    // sxd: evaluate_xpath(&doc, "count(//a)")  -- parses every time
    let q = XPath::compile("count(//a)").unwrap();   // once
    assert_eq!(q.evaluate(&doc).to_str(&doc), "2");  // many
}

/// From `doc/MIGRATION-FROM-SXD.md`, line 63.
#[rustfmt::skip]
#[test]
fn migration_from_sxd_md_line_63() {
    use oxml::{XPath, parse};

    let doc = parse("<r><a>text</a></r>").unwrap();
    let v = XPath::compile("//a").unwrap().evaluate(&doc);
    assert_eq!(v.to_str(&doc), "text");
    assert!(v.to_boolean());          // no document needed
}

/// From `doc/MIGRATION-FROM-SXD.md`, line 84.
#[rustfmt::skip]
#[test]
fn migration_from_sxd_md_line_84() {
    use oxml::{XPath, parse};

    let doc = parse(r#"<r xmlns:m="urn:u"><m:a>yes</m:a><a>no</a></r>"#).unwrap();

    // sxd: Context::new().set_namespace("m", "urn:u")
    let q = XPath::compile_with_namespaces("//m:a", &[("m", "urn:u")]).unwrap();
    assert_eq!(q.evaluate(&doc).to_str(&doc), "yes");
}

/// From `doc/SECURITY-MODEL.md`, line 39.
#[rustfmt::skip]
#[test]
fn security_model_md_line_39() {
    let src = r#"<!DOCTYPE a [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><a>&xxe;</a>"#;
    let doc = oxml::parse(src).expect("well-formed: the entity is declared");
    assert_eq!(doc.text(doc.root()).trim(), "");
}

/// From `doc/USER-GUIDE.md`, line 67.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_67() {
    use oxml::parse;

    let doc = parse("<r><a/><b/></r>").unwrap();

    let root = doc.root();                       // the document node
    let element = doc.root_element().unwrap();   // <r>
    assert_ne!(root, element);
}

/// From `doc/USER-GUIDE.md`, line 94.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_94() {
    use oxml::parse;

    let doc = parse("<r><a/><b/><a/></r>").unwrap();
    let a_elements: Vec<_> = doc
        .descendants()
        .filter(|&id| doc.element_name(id).is_some_and(|n| n.local == "a"))
        .collect();
    assert_eq!(a_elements.len(), 2);
}

/// From `doc/USER-GUIDE.md`, line 114.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_114() {
    use oxml::parse;

    let doc = parse("<p>The <em>first</em> finding.<!--note--></p>").unwrap();
    let p = doc.root_element().unwrap();
    assert_eq!(doc.text(p), "The first finding.");
}

/// From `doc/USER-GUIDE.md`, line 130.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_130() {
    use oxml::parse;

    let doc = parse(r#"<order id="A-1" note="two &amp; a half"/>"#).unwrap();
    let order = doc.root_element().unwrap();

    assert_eq!(doc.attribute(order, "id"), Some("A-1"));
    // Entities are already resolved.
    assert_eq!(doc.attribute(order, "note"), Some("two & a half"));
    assert_eq!(doc.attribute(order, "absent"), None);

    for attr in doc.attributes(order) {
        // Names are interned; resolve the handle through the document.
        let name = doc.name(attr.name).expect("interned");
        let _ = (&name.local, &attr.value);
    }
}

/// From `doc/USER-GUIDE.md`, line 152.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_152() {
    use oxml::{ExpandedName, parse};

    let doc = parse(r#"<a xmlns:x="urn:x" x:ref="R" ref="plain"/>"#).unwrap();
    let a = doc.root_element().unwrap();
    let wanted = ExpandedName::qualified("urn:x", "ref");
    let found = doc
        .attributes(a)
        .into_iter()
        .find(|at| doc.name(at.name) == Some(&wanted));
    assert_eq!(found.map(|at| at.value.as_str()), Some("R"));
}

/// From `doc/USER-GUIDE.md`, line 174.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_174() {
    use oxml::parse;

    let one = parse(r#"<a:x xmlns:a="urn:u"/>"#).unwrap();
    let two = parse(r#"<b:x xmlns:b="urn:u"/>"#).unwrap();
    assert_eq!(
        one.element_name(one.root_element().unwrap()),
        two.element_name(two.root_element().unwrap()),
    );
}

/// From `doc/USER-GUIDE.md`, line 196.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_196() {
    use oxml::{XPath, parse};

    let doc = parse("<shop><item price='9.99'/><item price='4.50'/></shop>").unwrap();
    let total = XPath::compile("sum(//item/@price)").unwrap();
    assert_eq!(total.evaluate(&doc).to_str(&doc), "14.49");
}

/// From `doc/USER-GUIDE.md`, line 210.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_210() {
    use oxml::{XPath, parse, xpath::Value};

    let doc = parse("<r><a/><a/></r>").unwrap();
    let nodes = XPath::compile("//a").unwrap().evaluate(&doc);
    assert!(matches!(nodes, Value::NodeSet(_)));
    assert_eq!(nodes.nodes().unwrap().len(), 2);
    assert!(nodes.to_boolean());              // non-empty
}

/// From `doc/USER-GUIDE.md`, line 227.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_227() {
    use oxml::{XPath, parse};

    let doc = parse("<shop><item p='1'>Tea</item><item p='2'>Cocoa</item></shop>").unwrap();
    let rows = XPath::compile("//item").unwrap();
    let price = XPath::compile("@p").unwrap();
    let mut total = 0.0;
    for &node in rows.evaluate(&doc).nodes().unwrap() {
        total += price.evaluate_from(&doc, node).to_number(&doc);
    }
    assert_eq!(total, 3.0);
}

/// From `doc/USER-GUIDE.md`, line 245.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_245() {
    use oxml::{ErrorKind, parse};

    let error = parse("<a></b>").unwrap_err();
    assert!(matches!(error.kind, ErrorKind::MismatchedEndTag { .. }));
    let (line, column) = error.line_column("<a></b>");
    assert_eq!((line, column), (1, 4));
}

/// From `doc/USER-GUIDE.md`, line 267.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_267() {
    use oxml::{Limits, parse_with};

    // Anything from a network.
    assert!(parse_with("<a/>", Limits::strict()).is_ok());

    // Or one field at a time. `Limits` is `#[non_exhaustive]`, so start
    // from a profile rather than writing a struct literal.
    let mut limits = Limits::default();
    limits.max_depth = 32;
    assert!(parse_with("<a><b/></a>", limits).is_ok());
}

/// From `doc/USER-GUIDE.md`, line 282.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_282() {
    use oxml::parse_bytes;

    let doc = parse_bytes(b"<?xml version='1.0'?><a>hi</a>").unwrap();
    assert_eq!(doc.text(doc.root()), "hi");
}

/// From `doc/USER-GUIDE.md`, line 316.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_316() {
    use oxml::parse;

    let doc = parse("<r><a id='1'/><a id='2'/></r>").unwrap();
    let root = doc.root_element().unwrap();
    std::thread::scope(|scope| {
        let handles: Vec<_> = (0..4)
            .map(|_| scope.spawn(|| doc.children(root).len()))
            .collect();
        for handle in handles {
            assert_eq!(handle.join().unwrap(), 2);
        }
    });
}

/// From `doc/USER-GUIDE.md`, line 335.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_335() {
    use oxml::{XPath, parse};

    let doc = parse(r#"<r><a href="one"/><a href="two"/></r>"#).unwrap();
    let q = XPath::compile("//@href").unwrap();
    let values: Vec<_> = q
        .evaluate(&doc)
        .nodes()
        .unwrap()
        .iter()
        .map(|&n| doc.text(n))
        .collect();
    assert_eq!(values, ["one", "two"]);
}

/// From `doc/USER-GUIDE.md`, line 352.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_352() {
    use oxml::parse;

    let doc = parse("<r><meta/><body>text</body></r>").unwrap();
    let body = doc
        .descendants()
        .find(|&id| doc.element_name(id).is_some_and(|n| n.local == "body"));
    assert_eq!(body.map(|id| doc.text(id)), Some("text".to_string()));
}

/// From `doc/USER-GUIDE.md`, line 364.
#[rustfmt::skip]
#[test]
fn user_guide_md_line_364() {
    use oxml::parse;

    fn check(input: &str) -> Result<(), String> {
        match parse(input) {
            Ok(_) => Ok(()),
            Err(e) => {
                let (line, column) = e.line_column(input);
                Err(format!("{line}:{column}: {}", e.kind))
            }
        }
    }

    assert_eq!(check("<a/>"), Ok(()));
    assert!(check("<a>").is_err());
}