use pel::runtime::value::{DocumentBuilder, QualifiedName, Value as PelValue};
use std::rc::Rc;
use log::debug;
use roxmltree::{Document, Node, NodeType};
pub fn xml_to_pel(body: String) -> PelValue {
let body = Rc::new(body);
match Document::parse(body.as_ref()) {
Ok(doc) => doc
.root()
.first_child()
.and_then(|node| transverse(&node, Rc::clone(&body), None))
.map(|d| d.build())
.unwrap_or_else(PelValue::null),
Err(err) => {
debug!("Unexpected error parsing xml body: {err}");
PelValue::null()
}
}
}
fn filter_nodes(node: &Node) -> bool {
match node.node_type() {
NodeType::Element => true,
NodeType::Text => node
.text()
.map(|t| !t.trim().is_empty())
.unwrap_or_default(),
_ => false,
}
}
fn transverse(
node: &Node,
origin: Rc<String>,
parent: Option<DocumentBuilder>,
) -> Option<DocumentBuilder> {
match node.node_type() {
NodeType::Element => {
let default_namespace = node.default_namespace().unwrap_or_default();
let namespace = node.tag_name().namespace().unwrap_or(default_namespace);
let prefix = node
.namespaces()
.find(|n| n.uri().eq(namespace))
.and_then(|n| n.name());
let name = QualifiedName::prefixed(
prefix.unwrap_or_default(),
namespace,
node.tag_name().name(),
);
let mut builder = match parent {
None => DocumentBuilder::root(name, Rc::clone(&origin), node.range()),
Some(b) => b.with_element(name, node.range()),
};
for attr in node.attributes() {
let namespace = attr.namespace().unwrap_or(default_namespace);
let prefix = node
.namespaces()
.find(|n| n.uri().eq(namespace))
.and_then(|n| n.name());
let attr_name =
QualifiedName::prefixed(prefix.unwrap_or_default(), namespace, attr.name());
builder = builder.with_attribute(attr_name, attr.value())
}
for child in node.children().filter(filter_nodes) {
builder = transverse(&child, Rc::clone(&origin), Some(builder))
.expect("Builder already initialized.");
}
builder = builder.finish_element();
Some(builder)
}
NodeType::Text => parent.map(|builder| builder.with_text(node.text().unwrap_or_default())),
_ => parent,
}
}