use xot::{Node, Xot};
use crate::{atomic, error};
use super::{core::Sequence, item::Item};
enum NodeOrString {
Node(Node),
String(String),
}
pub(crate) fn normalize(
sequence: &Sequence,
item_separator: &str,
xot: &mut Xot,
) -> error::Result<Node> {
let sequence = if !sequence.is_empty() {
sequence.flatten()?
} else {
let atom: atomic::Atomic = "".into();
Sequence::from(vec![atom])
};
let mut items: Vec<NodeOrString> = Vec::new();
for item in sequence.iter() {
match item {
Item::Atomic(atomic) => {
let s = atomic.clone().into_canonical();
if let Some(NodeOrString::String(last_s)) = items.last_mut() {
last_s.push_str(item_separator);
last_s.push_str(&s);
} else {
items.push(NodeOrString::String(s));
}
}
Item::Node(node) => {
items.push(NodeOrString::Node(node));
}
Item::Function(_) => {
return Err(error::Error::SENR0001);
}
}
}
let mut flattened_nodes = Vec::new();
for item in items {
match item {
NodeOrString::Node(node) => {
let node = xot.clone_with_prefixes(node);
if matches!(xot.value(node), xot::Value::Document) {
for child in xot.children(node) {
flattened_nodes.push(child);
}
continue;
}
flattened_nodes.push(node);
}
NodeOrString::String(s) => {
let text = xot.new_text(&s);
flattened_nodes.push(text);
}
}
}
let mut nodes = Vec::new();
for node in flattened_nodes {
match xot.value(node) {
xot::Value::Text(text) => {
let text = text.get();
if !text.is_empty() {
let text = text.to_string();
if let Some(last_node) = nodes.last_mut() {
if let Some(last_text) = xot.text_mut(*last_node) {
let new_text = format!("{}{}", last_text.get(), text);
last_text.set(new_text);
continue;
}
}
} else {
continue;
}
}
xot::Value::Document => {
unreachable!("Documents should have been flattened by this point");
}
xot::Value::Attribute(_) | xot::Value::Namespace(_) => {
return Err(error::Error::SENR0001);
}
_ => {}
}
nodes.push(node);
}
let document = xot.new_document();
for node in nodes {
xot.append(document, node).unwrap();
}
Ok(document)
}