///|
/// A parsed XML document: its single root element.
///
/// The declaration, processing instructions, DOCTYPE and comments are not
/// represented because the underlying reader skips them.
pub struct Document {
/// The document's root element.
root : Element
} derive(Debug, Eq)
///|
/// An element node: a qualified tag name, its attributes and its children
/// in document order.
pub(all) struct Element {
/// Raw qualified tag name, e.g. `"itunes:author"` or `"title"`.
name : String
/// Attributes in document order; values are entity-decoded.
attrs : Array[Attribute]
/// Child nodes in document order.
children : Array[Node]
} derive(Debug, Eq)
///|
/// A child node of an element.
pub(all) enum Node {
/// Character data with entities decoded; adjacent character data is
/// merged into a single node by the builder.
Text(String)
/// The verbatim contents of a `` section.
CData(String)
/// A child element.
Element(Element)
} derive(Debug, Eq)
///|
/// Create an element.
pub fn Element::new(
name~ : String,
attrs? : Array[Attribute] = [],
children? : Array[Node] = [],
) -> Element {
{ name, attrs, children }
}
///|
/// The local part of the qualified tag name:
/// `"itunes:author"` → `"author"`.
pub fn Element::local_name(self : Element) -> String {
local_name(self.name)
}
///|
/// The value of the attribute named `key`, or `None` when absent.
pub fn Element::attr(self : Element, key : String) -> String? {
for a in self.attrs {
if a.key == key {
return Some(a.value)
}
}
None
}
///|
/// The direct child elements, in document order.
pub fn Element::elements(self : Element) -> Array[Element] {
let out : Array[Element] = []
for child in self.children {
match child {
Element(el) => out.push(el)
_ => ()
}
}
out
}
///|
/// The first direct child element whose raw tag name equals `name`,
/// or `None` when absent. Use [`Element::elements`] and
/// [`Element::local_name`] for prefix-insensitive lookups.
pub fn Element::element(self : Element, name : String) -> Element? {
for el in self.elements() {
if el.name == name {
return Some(el)
}
}
None
}
///|
/// The concatenated character data of all descendant `Text` and `CData`
/// nodes, like DOM's `textContent`.
pub fn Element::text_content(self : Element) -> String {
let sb = StringBuilder()
fn collect(node : Node) {
match node {
Text(s) => sb.write_string(s)
CData(s) => sb.write_string(s)
Element(el) =>
for child in el.children {
collect(child)
}
}
}
for child in self.children {
collect(child)
}
sb.to_string()
}