use crate::xotdata::{Node, Xot};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Document {
pub before: Vec<DocumentContent>,
pub document_element: Element,
pub after: Vec<DocumentContent>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Name {
pub namespace: String,
pub localname: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Prefix {
pub name: String,
pub namespace: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Element {
pub name: Name,
pub prefixes: Vec<Prefix>,
pub attributes: Vec<(Name, String)>,
pub children: Vec<Content>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Content {
Text(String),
Comment(String),
ProcessingInstruction(ProcessingInstruction),
Element(Element),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DocumentContent {
Comment(String),
ProcessingInstruction(ProcessingInstruction),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ProcessingInstruction {
pub target: String,
pub content: Option<String>,
}
impl Name {
pub fn xotify(&self, xot: &mut Xot) -> crate::NameId {
let namespace = xot.add_namespace(&self.namespace);
xot.add_name_ns(&self.localname, namespace)
}
}
impl Document {
pub fn xotify(&self, xot: &mut Xot) -> Node {
let child = self.document_element.xotify(xot);
let document = xot.new_document_with_element(child).unwrap();
for content in &self.before {
let node = create_document_content_node(xot, content);
xot.insert_before(child, node).unwrap();
}
for content in &self.after {
let node = create_document_content_node(xot, content);
xot.append(child, node).unwrap();
}
document
}
}
impl Element {
pub fn xotify(&self, xot: &mut Xot) -> Node {
let name = self.name.xotify(xot);
let prefixes = self
.prefixes
.iter()
.map(|prefix| {
let prefix_id = xot.add_prefix(&prefix.name);
let ns_id = xot.add_namespace(&prefix.namespace);
(prefix_id, ns_id)
})
.collect::<Vec<_>>();
let attributes = self
.attributes
.iter()
.map(|(name, value)| {
let ns_id = xot.add_namespace(&name.namespace);
let name_id = xot.add_name_ns(&name.localname, ns_id);
(name_id, value)
})
.collect::<Vec<_>>();
let element_node = xot.new_element(name);
let mut namespaces_map = xot.namespaces_mut(element_node);
for (prefix, ns) in prefixes {
namespaces_map.insert(prefix, ns);
}
let mut attributes_map = xot.attributes_mut(element_node);
for (name, value) in attributes {
attributes_map.insert(name, value.clone());
}
let children = self
.children
.iter()
.map(|child| child.xotify(xot))
.collect::<Vec<_>>();
for child in children {
xot.append(element_node, child).unwrap();
}
element_node
}
}
impl ProcessingInstruction {
pub fn xotify(&self, xot: &mut Xot) -> Node {
let target = xot.add_name(&self.target);
xot.new_processing_instruction(target, self.content.as_deref())
}
}
impl Content {
fn xotify(&self, xot: &mut Xot) -> Node {
match self {
Content::Text(text) => xot.new_text(text),
Content::Comment(comment) => xot.new_comment(comment),
Content::ProcessingInstruction(processing_instruction) => {
processing_instruction.xotify(xot)
}
Content::Element(element) => element.xotify(xot),
}
}
}
fn create_document_content_node(xot: &mut Xot, content: &DocumentContent) -> Node {
match content {
DocumentContent::Comment(comment) => xot.new_comment(comment),
DocumentContent::ProcessingInstruction(processing_instruction) => {
processing_instruction.xotify(xot)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xotify() {
let mut xot = Xot::new();
let document = Document {
before: vec![],
document_element: Element {
name: Name {
namespace: "".to_string(),
localname: "foo".to_string(),
},
attributes: vec![],
prefixes: vec![],
children: vec![Content::Text("Example".to_string())],
},
after: vec![],
};
let document = document.xotify(&mut xot);
assert_eq!(xot.to_string(document).unwrap(), "<foo>Example</foo>");
}
}