use std::fmt::{Display, Formatter};
use std::ops::Range;
use std::rc::Rc;
use crate::runtime::value::{InternalValue, Value};
#[derive(Debug)]
struct InnerDocument {
origin: Rc<String>,
nodes: Vec<InnerDocNode>,
}
impl InnerDocument {
fn new(origin: Rc<String>) -> Self {
InnerDocument {
origin,
nodes: vec![],
}
}
fn get(&self, pos: usize) -> Option<&InnerDocNode> {
self.nodes.get(pos)
}
fn len(&self) -> usize {
self.nodes.len()
}
}
type Attributes = Vec<(QualifiedName, String)>;
#[derive(Debug, Clone)]
pub struct DocNode {
doc: Rc<InnerDocument>,
pos: usize,
}
impl PartialEq for DocNode {
fn eq(&self, other: &Self) -> bool {
self.inner()
.eq(other.inner(), self.doc.as_ref(), other.doc.as_ref())
}
}
impl Display for DocNode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.do_fmt(self.pos, 0, f, &mut vec![])
}
}
fn add_prefix<'a>(
f: &mut Formatter<'_>,
name: &'a QualifiedName,
namespaces: &mut Vec<(&'a str, &'a str)>,
pushed: &mut Vec<usize>,
) -> std::fmt::Result {
if name.namespace.is_empty() {
return Ok(());
}
let mut iter = namespaces.iter();
while let Some((prefix, namespace)) = iter.next_back() {
if prefix.eq(&name.prefix) && namespace.eq(&name.namespace) {
return Ok(());
} else if prefix.eq(&name.prefix) {
break;
}
}
pushed.push(namespaces.len());
namespaces.push((&name.prefix, &name.namespace));
match name.prefix.is_empty() {
true => f.write_fmt(format_args!(" xmlns=\"{}\"", name.namespace)),
false => f.write_fmt(format_args!(
" xmlns:{}=\"{}\"",
name.prefix, name.namespace
)),
}
}
const PADDING: &str = " ";
fn pad(f: &mut Formatter<'_>, level: usize) -> std::fmt::Result {
for _ in 0..level {
f.write_str(PADDING)?
}
Ok(())
}
impl DocNode {
fn do_fmt<'a>(
&'a self,
node: usize,
level: usize,
f: &mut Formatter<'_>,
namespaces: &mut Vec<(&'a str, &'a str)>,
) -> std::fmt::Result {
match self.node(node) {
InnerDocNode::InnerElement(e) => {
let mut pushed = vec![];
pad(f, level)?;
f.write_fmt(format_args!("<{}", e.name))?;
add_prefix(f, &e.name, namespaces, &mut pushed)?;
for (name, value) in &e.attributes {
add_prefix(f, name, namespaces, &mut pushed)?;
f.write_fmt(format_args!(" {name}=\"{value}\""))?;
}
if e.children.is_empty() {
f.write_str("/>")?;
} else {
f.write_str(">")?;
for child in &e.children {
f.write_str("\n")?;
self.do_fmt(*child, level + 1, f, namespaces)?;
}
f.write_str("\n")?;
pad(f, level)?;
f.write_fmt(format_args!("</{}>", e.name))?;
}
while let Some(push) = pushed.pop() {
namespaces.remove(push);
}
Ok(())
}
InnerDocNode::Leaf(e) => {
pad(f, level)?;
f.write_str(&e.value)
}
}
}
fn inner(&self) -> &InnerDocNode {
self.doc.get(self.pos).unwrap()
}
fn node(&self, pos: usize) -> &InnerDocNode {
self.doc.get(pos).unwrap()
}
pub fn child_by_name(&self, key: &str) -> Option<DocNode> {
if let InnerDocNode::InnerElement(inner) = self.inner() {
for child_pos in &inner.children {
if let InnerDocNode::InnerElement(e) = self.node(*child_pos) {
if e.name.local_name.eq(key) {
return Some(DocNode {
doc: Rc::clone(&self.doc),
pos: *child_pos,
});
}
}
}
}
None
}
pub fn child_by_qname(&self, qname: &QualifiedName) -> Option<DocNode> {
if let InnerDocNode::InnerElement(inner) = self.inner() {
for child_pos in &inner.children {
if let InnerDocNode::InnerElement(e) = self.node(*child_pos) {
if e.name.eq(qname) {
return Some(DocNode {
doc: Rc::clone(&self.doc),
pos: *child_pos,
});
}
}
}
}
None
}
pub fn attr_by_name(&self, key: &str) -> Option<&str> {
match self.inner() {
InnerDocNode::InnerElement(inner) => inner
.attributes
.iter()
.filter(|(name, _)| name.local_name.eq(key))
.map(|(_, val)| val.as_str())
.next(),
_ => None,
}
}
pub fn attr_by_qname(&self, qname: &QualifiedName) -> Option<&str> {
match self.inner() {
InnerDocNode::InnerElement(inner) => inner
.attributes
.iter()
.filter(|(name, _)| name.eq(qname))
.map(|(_, val)| val.as_str())
.next(),
_ => None,
}
}
pub fn child_by_index(&self, index: usize) -> Option<DocNode> {
if let InnerDocNode::InnerElement(inner) = self.inner() {
if index < inner.children.len() {
return Some(DocNode {
doc: Rc::clone(&self.doc),
pos: *inner.children.get(index).unwrap(),
});
}
}
None
}
pub fn childs_by_name(&self, key: &str) -> Vec<DocNode> {
let mut childs = Vec::new();
if let InnerDocNode::InnerElement(inner) = self.inner() {
for child_pos in &inner.children {
if let InnerDocNode::InnerElement(e) = self.node(*child_pos) {
if e.name.local_name.eq(key) {
childs.push(DocNode {
doc: Rc::clone(&self.doc),
pos: *child_pos,
});
}
}
}
}
childs
}
pub fn childs_by_qname(&self, qname: &QualifiedName) -> Vec<DocNode> {
let mut childs = Vec::new();
if let InnerDocNode::InnerElement(inner) = self.inner() {
for child_pos in &inner.children {
if let InnerDocNode::InnerElement(e) = self.node(*child_pos) {
if e.name.eq(qname) {
childs.push(DocNode {
doc: Rc::clone(&self.doc),
pos: *child_pos,
});
}
}
}
}
childs
}
pub(super) fn compare(&self, other: &InternalValue) -> bool {
match other {
InternalValue::Null => self.is_null(),
InternalValue::String(s) => self.as_str().map(|inner| inner.eq(s)).unwrap_or_default(),
InternalValue::Node(other) => self.compare_children(other), InternalValue::Array(_) => false, InternalValue::Object(_) => false, _ => false,
}
}
fn compare_children(&self, other: &DocNode) -> bool {
match (self.inner(), other.inner()) {
(InnerDocNode::InnerElement(first), InnerDocNode::InnerElement(second)) => {
first.compare_children(second, self.doc.as_ref(), other.doc.as_ref())
}
_ => false,
}
}
pub fn is_null(&self) -> bool {
match self.inner() {
InnerDocNode::InnerElement(i) => i.children.is_empty(),
InnerDocNode::Leaf(_) => false,
}
}
pub fn as_str(&self) -> Option<&str> {
match self.inner() {
InnerDocNode::Leaf(l) => Some(&l.value),
InnerDocNode::InnerElement(e) => {
if e.children.len() == 1 {
match self.node(*e.children.first().unwrap()) {
InnerDocNode::InnerElement(_) => None,
InnerDocNode::Leaf(leaf) => Some(&leaf.value),
}
} else {
None
}
}
}
}
pub fn content(&self) -> Option<String> {
match self.inner() {
InnerDocNode::InnerElement(e) => {
if e.parent.is_none() {
self.doc.origin.get(e.range.clone()).map(|e| e.to_string())
} else if e.children.len() == 1 {
let node = DocNode {
doc: Rc::clone(&self.doc),
pos: *e.children.first().unwrap(),
};
Some(format!("<?xml version='1.0' encoding='UTF-8'?>\n{node}"))
} else {
None
}
}
InnerDocNode::Leaf(l) => Some(l.value.clone()),
}
}
pub fn is_empty(&self) -> bool {
match self.inner() {
InnerDocNode::Leaf(l) => l.value.is_empty(),
InnerDocNode::InnerElement(e) => {
if e.children.len() == 1 {
match self.node(*e.children.first().unwrap()) {
InnerDocNode::InnerElement(_) => false,
InnerDocNode::Leaf(leaf) => leaf.value.is_empty(),
}
} else {
e.children.len() != 0
}
}
}
}
pub fn size(&self) -> Option<usize> {
match self.inner() {
InnerDocNode::Leaf(l) => Some(l.value.len()),
InnerDocNode::InnerElement(e) => {
if e.children.len() == 1 {
match self.node(*e.children.first().unwrap()) {
InnerDocNode::InnerElement(_) => Some(e.children.len()),
InnerDocNode::Leaf(l) => Some(l.value.len()),
}
} else {
Some(e.children.len())
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct QualifiedName {
pub(crate) prefix: String,
pub(crate) namespace: String,
local_name: String,
}
impl PartialEq for QualifiedName {
fn eq(&self, other: &Self) -> bool {
self.namespace.eq(&other.namespace) && self.local_name.eq(&other.local_name)
}
}
impl Display for QualifiedName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.prefix.is_empty() {
true => f.write_fmt(format_args!("{}", self.local_name)),
false => f.write_fmt(format_args!("{}:{}", self.prefix, self.local_name)),
}
}
}
impl QualifiedName {
pub fn prefixed<P: Into<String>, N: Into<String>, L: Into<String>>(
prefix: P,
namespace: N,
local_name: L,
) -> Self {
Self {
prefix: prefix.into(),
namespace: namespace.into(),
local_name: local_name.into(),
}
}
pub fn new<T: Into<String>, K: Into<String>>(namespace: T, local_name: K) -> Self {
Self {
prefix: Default::default(),
namespace: namespace.into(),
local_name: local_name.into(),
}
}
}
#[derive(Debug)]
enum InnerDocNode {
InnerElement(InnerElement),
Leaf(Leaf),
}
impl InnerDocNode {
fn eq(&self, other: &Self, self_array: &InnerDocument, other_array: &InnerDocument) -> bool {
match (self, other) {
(InnerDocNode::InnerElement(first), InnerDocNode::InnerElement(second)) => {
first.eq(second, self_array, other_array)
}
(InnerDocNode::Leaf(first), InnerDocNode::Leaf(second)) => first.eq(second),
_ => false,
}
}
}
#[derive(Debug)]
struct InnerElement {
name: QualifiedName,
parent: Option<usize>,
range: Range<usize>,
attributes: Attributes,
children: Vec<usize>,
}
impl InnerElement {
fn eq(&self, other: &Self, self_array: &InnerDocument, other_array: &InnerDocument) -> bool {
self.name.eq(&other.name)
&& self.attributes.eq(&other.attributes)
&& self.compare_children(other, self_array, other_array)
}
fn compare_children(
&self,
other: &Self,
self_array: &InnerDocument,
other_array: &InnerDocument,
) -> bool {
self.children.len() == other.children.len()
&& self
.children
.iter()
.zip(other.children.iter())
.all(|(first, second)| {
self_array.get(*first).unwrap().eq(
other_array.get(*second).unwrap(),
self_array,
other_array,
)
})
}
}
#[derive(Debug)]
struct Leaf {
_parent: usize,
value: String,
}
impl Leaf {
fn eq(&self, other: &Self) -> bool {
self.value.eq(&other.value)
}
}
pub struct DocumentBuilder {
doc: InnerDocument,
parent_builder: Vec<usize>,
}
impl DocumentBuilder {
pub(crate) fn new(origin: Rc<String>) -> Self {
DocumentBuilder {
doc: InnerDocument::new(origin),
parent_builder: vec![],
}
}
pub fn root(name: QualifiedName, origin: Rc<String>, range: Range<usize>) -> DocumentBuilder {
let builder = DocumentBuilder::new(origin);
builder
.add(QualifiedName::new("", ""), range.clone()) .add(name, range)
}
fn add(mut self, name: QualifiedName, range: Range<usize>) -> Self {
let pos = self.doc.len();
let parent_pos = self.parent_builder.last().cloned();
if let Some(parent_pos) = parent_pos {
if let Some(InnerDocNode::InnerElement(e)) = self.doc.nodes.get_mut(parent_pos) {
e.children.push(pos)
}
}
let element = InnerElement {
name,
parent: parent_pos,
range,
attributes: vec![],
children: vec![],
};
self.doc.nodes.push(InnerDocNode::InnerElement(element));
self.parent_builder.push(pos);
self
}
pub fn with_attribute<K: Into<String>>(mut self, name: QualifiedName, value: K) -> Self {
if let Some(InnerDocNode::InnerElement(e)) = self.doc.nodes.get_mut(
self.parent_builder
.last()
.cloned()
.expect("Adding attribute to non existing element"),
) {
e.attributes.push((name, value.into()))
}
self
}
pub fn with_element(self, name: QualifiedName, range: Range<usize>) -> Self {
self.add(name, range)
}
pub fn with_text<T: Into<String>>(mut self, value: T) -> Self {
let pos = self.doc.len();
let parent_pos = self
.parent_builder
.last()
.cloned()
.expect("Adding text to the root element");
if let Some(InnerDocNode::InnerElement(e)) = self.doc.nodes.get_mut(parent_pos) {
e.children.push(pos)
}
let leaf = Leaf {
_parent: parent_pos,
value: value.into(),
};
self.doc.nodes.push(InnerDocNode::Leaf(leaf));
self
}
pub fn finish_element(mut self) -> DocumentBuilder {
if self.parent_builder.len() <= 1 {
panic!("First element must be finished with finish_doc.")
}
self.parent_builder.pop();
self
}
pub fn build(self) -> Value {
if self.parent_builder.len() != 1 {
panic!("Finishing document before closing all elements.")
}
#[cfg(feature = "experimental_coerced_type")]
let origin = Rc::clone(&self.doc.origin);
#[cfg(feature = "experimental_coerced_type")]
return Value::coerced_node(
DocNode {
doc: Rc::new(self.doc),
pos: 0,
},
origin,
);
#[cfg(not(feature = "experimental_coerced_type"))]
return Value::node(DocNode {
doc: Rc::new(self.doc),
pos: 0,
});
}
}