Skip to main content

muffy_document/document/
node.rs

1use super::element::Element;
2use alloc::sync::Arc;
3use html5ever::{QualName, ns};
4use markup5ever_rcdom::NodeData;
5
6// TODO Is it valid to handle all markup languages with a single driver schema?
7const DEFAULT_NAMESPACES: &[&str] = &[
8    "",
9    "http://www.w3.org/1998/Math/MathML",
10    "http://www.w3.org/1999/xhtml",
11    "http://www.w3.org/2000/svg",
12];
13const NAMESPACE_PREFIXES: &[(&str, &str)] = &[
14    ("http://creativecommons.org/ns#", "cc"),
15    ("http://purl.org/dc/elements/1.1/", "dc"),
16    (
17        "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
18        "sodipodi",
19    ),
20    ("http://www.inkscape.org/namespaces/inkscape", "inkscape"),
21    ("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf"),
22    ("http://www.w3.org/1999/xlink", "xlink"),
23    ("http://www.w3.org/XML/1998/namespace", "xml"),
24];
25
26/// A node.
27#[derive(Debug, Eq, PartialEq)]
28pub enum Node {
29    /// An element.
30    Element(Element),
31    /// A text.
32    Text(String),
33}
34
35impl Node {
36    pub(crate) fn from_markup5ever(node: &markup5ever_rcdom::Node) -> Option<Self> {
37        match &node.data {
38            NodeData::Element { name, attrs, .. } => Some(Self::Element(
39                Element::new(
40                    qualify_name(name),
41                    attrs
42                        .borrow()
43                        .iter()
44                        // Namespace declarations on foreign elements are not
45                        // semantic attributes.
46                        .filter(|attribute| attribute.name.ns != ns!(xmlns))
47                        .map(|attribute| {
48                            (qualify_name(&attribute.name), attribute.value.to_string())
49                        })
50                        .collect(),
51                    node.children
52                        .borrow()
53                        .iter()
54                        .flat_map(|node| Self::from_markup5ever(node))
55                        .map(Arc::new)
56                        .collect(),
57                )
58                .set_namespace((!name.ns.is_empty()).then(|| name.ns.to_string())),
59            )),
60            NodeData::Text { contents } => Some(Self::Text(contents.borrow().to_string())),
61            NodeData::Comment { .. }
62            | NodeData::Document
63            | NodeData::Doctype { .. }
64            | NodeData::ProcessingInstruction { .. } => None,
65        }
66    }
67}
68
69fn qualify_name(name: &QualName) -> String {
70    if let Some(prefix) = NAMESPACE_PREFIXES
71        .iter()
72        .find_map(|(namespace, prefix)| (*name.ns == **namespace).then_some(prefix))
73    {
74        format!("{prefix}:{}", name.local)
75    } else if DEFAULT_NAMESPACES.contains(&&*name.ns) {
76        name.local.to_string()
77    } else if let Some(prefix) = &name.prefix {
78        format!("{prefix}:{}", name.local)
79    } else {
80        name.local.to_string()
81    }
82}