/// elements resemble a final tree, including inherited namespace information
use std::{
collections::{HashMap, HashSet, VecDeque},
str::FromStr,
};
use tracing::trace;
use crate::{
error::{DeserializeError, Error},
Result,
};
pub type DeserializeResult<T> = std::result::Result<T, DeserializeError>;
pub trait FromElement: Sized {
fn from_element(element: Element) -> DeserializeResult<Self>;
}
pub trait IntoElement {
fn builder(&self) -> ElementBuilder;
fn into_element(&self) -> Element {
self.builder().build().unwrap()
}
fn get_content(&self) -> VecDeque<Content> {
let element = self.into_element();
element.content
}
}
// when are namespaces names chosen then if they are automatically calculated
// namespaces are held by readers and writers.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct NamespaceDeclaration {
pub prefix: Option<String>,
pub namespace: String,
}
// names are qualified, they contain a reference to the namespace (held within the reader/writer)
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Name {
pub namespace: Option<String>,
pub local_name: String,
}
#[derive(Debug, Clone)]
pub enum Content {
Element(Element),
Text(String),
PI,
Comment(String),
}
// should this be a trait?
#[derive(Debug, Clone)]
pub struct Element {
pub name: Name,
// namespace: Name,
// each element once created contains the qualified namespace information for that element
// the name contains the qualified namespace so this is unnecessary
// namespace: String,
// hashmap of explicit namespace declarations on the element itself only
// possibly not needed as can be calculated at write time depending on context and qualified namespace, and for reading, element validity and namespaces are kept track of by the reader.
// change this to custom namespace declarations only, so you can override the definition of namespaces if you wish
pub namespace_declaration_overrides: HashSet<NamespaceDeclaration>,
// attributes can be in a different namespace than the element. how to make sure they are valid?
// maybe include the namespace instead of or with the prefix
// you can calculate the prefix from the namespaced name and the current writer context
// you can validate the prefix and calculate the namespace from the current reader context
// this results in readers and writers being able to return qualification errors as they aren't able to create elements until every part is qualified.
pub attributes: HashMap<Name, String>,
// TODO: make a hashmap maybe? to be able to address parts of the content individually
pub content: VecDeque<Content>,
}
impl Element {
pub fn identify(&self) -> (Option<&str>, &str) {
(self.name.namespace.as_deref(), &self.name.local_name)
}
pub fn check_name(&self, name: &str) -> DeserializeResult<()> {
if self.name.local_name == name {
Ok(())
} else {
return Err(DeserializeError::IncorrectName {
expected: name.to_string(),
found: self.name.local_name.clone(),
});
}
}
pub fn check_namespace(&self, namespace: &str) -> DeserializeResult<()> {
if self.name.namespace.as_deref() == Some(namespace) {
return Ok(());
} else {
if let Some(actual_namespace) = &self.name.namespace {
return Err(DeserializeError::IncorrectNamespace {
expected: namespace.to_string(),
found: actual_namespace.clone(),
});
} else {
return Err(DeserializeError::Unqualified {
expected: namespace.to_string(),
});
}
}
}
pub fn attribute_opt<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<Option<V>> {
if let Some(att_value) = self.attributes.remove(&Name {
namespace: None,
local_name: att_name.to_string(),
}) {
let value = <V as FromStr>::from_str(&att_value)
.map_err(|_| DeserializeError::FromStr(att_value))?;
return Ok(Some(value));
} else {
return Ok(None);
}
}
pub fn attribute_opt_namespaced<V: FromStr>(
&mut self,
att_name: &str,
att_namespace: &str,
) -> DeserializeResult<Option<V>> {
if let Some(att_value) = self.attributes.remove(&Name {
namespace: Some(att_namespace.to_string()),
local_name: att_name.to_string(),
}) {
let value = <V as FromStr>::from_str(&att_value)
.map_err(|_| DeserializeError::FromStr(att_value))?;
return Ok(Some(value));
} else {
return Ok(None);
}
}
pub fn attribute<V: FromStr>(&mut self, att_name: &str) -> DeserializeResult<V> {
let name = Name {
namespace: None,
local_name: att_name.to_string(),
};
if let Some(att_value) = self.attributes.remove(&name) {
let value = <V as FromStr>::from_str(&att_value)
.map_err(|_| DeserializeError::FromStr(att_value))?;
return Ok(value);
} else {
return Err(DeserializeError::MissingAttribute(name));
}
}
pub fn attribute_namespaced<V: FromStr>(
&mut self,
att_name: &str,
att_namespace: &str,
) -> DeserializeResult<V> {
let name = Name {
namespace: Some(att_namespace.to_string()),
local_name: att_name.to_string(),
};
if let Some(att_value) = self.attributes.remove(&name) {
let value = <V as FromStr>::from_str(&att_value)
.map_err(|_| DeserializeError::FromStr(att_value))?;
return Ok(value);
} else {
return Err(DeserializeError::MissingAttribute(name));
}
}
pub fn no_more_attributes(self) -> DeserializeResult<Self> {
if self.attributes.is_empty() {
Ok(self)
} else {
Err(DeserializeError::UnexpectedAttributes(self.attributes))
}
}
// for xs:any
pub fn child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
Content::Text(_) => false,
Content::PI => false,
Content::Comment(_) => false,
}) {
let element = self.content.remove(position).unwrap();
if let Content::Element(e) = element {
return <T as FromElement>::from_element(e);
} else {
return Err(DeserializeError::MissingChild);
}
} else {
return Err(DeserializeError::MissingChild);
}
}
pub fn child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(element) => <T as FromElement>::from_element(element.clone()).is_ok(),
Content::Text(_) => false,
Content::PI => false,
Content::Comment(_) => false,
}) {
let element = self.content.remove(position).unwrap();
if let Content::Element(e) = element {
return Ok(Some(<T as FromElement>::from_element(e)?));
} else {
return Err(DeserializeError::MissingChild);
}
} else {
return Ok(None);
}
}
pub fn children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
let (children, rest): (VecDeque<_>, VecDeque<_>) = self
.content
.clone()
.into_iter()
.partition(|content| match content {
Content::Element(element) => {
<T as FromElement>::from_element(element.clone()).is_ok()
}
Content::Text(_) => false,
Content::PI => false,
Content::Comment(_) => false,
});
self.content = rest;
let children: Vec<T> = children
.into_iter()
.map(|content| {
let child = match content {
Content::Element(element) => <T as FromElement>::from_element(element).ok(),
Content::Text(_) => None,
Content::PI => None,
Content::Comment(_) => None,
}
.unwrap();
child
})
.collect();
Ok(children)
}
pub fn value<V: FromStr>(&mut self) -> DeserializeResult<V> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(_) => false,
Content::Text(s) => <V as FromStr>::from_str(s).is_ok(),
Content::PI => false,
Content::Comment(_) => false,
}) {
let element = self.content.remove(position).unwrap();
if let Content::Text(v) = element {
return Ok(<V as FromStr>::from_str(&v).ok().unwrap());
} else {
panic!("infallible")
}
} else {
return Err(DeserializeError::MissingValue);
}
}
pub fn value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
if let Some(position) = self.content.iter().position(|content| match content {
Content::Element(_) => false,
Content::Text(s) => <V as FromStr>::from_str(s).is_ok(),
Content::PI => false,
Content::Comment(_) => false,
}) {
let element = self.content.remove(position).unwrap();
if let Content::Text(v) = element {
return Ok(<V as FromStr>::from_str(&v).ok());
} else {
panic!("infallible")
}
} else {
return Ok(None);
}
}
// for xs:sequence
pub fn pop_child_one<T: FromElement>(&mut self) -> DeserializeResult<T> {
loop {
let child = self
.content
.pop_front()
.ok_or(DeserializeError::MissingChild)?;
match child {
Content::Element(element) => return Ok(<T as FromElement>::from_element(element)?),
Content::Text(_) => {
return Err(DeserializeError::UnexpectedContent(self.content.clone()))
}
Content::PI => {}
Content::Comment(_) => {}
}
}
}
pub fn pop_child_opt<T: FromElement>(&mut self) -> DeserializeResult<Option<T>> {
loop {
let child = self.content.pop_front();
if let Some(child) = child {
match child {
Content::Element(element) => {
return Ok(Some(<T as FromElement>::from_element(element)?))
}
Content::Text(_) => {
return Err(DeserializeError::UnexpectedContent(self.content.clone()))
}
Content::PI => {}
Content::Comment(_) => {}
}
} else {
return Ok(None);
}
}
}
pub fn pop_children<T: FromElement>(&mut self) -> DeserializeResult<Vec<T>> {
let mut children = Vec::new();
loop {
let child = self.content.front();
trace!("child: {:?}", child);
if let Some(child) = child {
match child {
Content::Element(element) => {
if let Ok(child) = <T as FromElement>::from_element(element.clone()) {
trace!("parsed child");
children.push(child);
self.content.pop_front();
} else {
trace!("failed to parse child");
return Ok(children);
}
}
Content::Text(_) => return Ok(children),
Content::PI => {
self.content.pop_front();
continue;
}
Content::Comment(_) => {
self.content.pop_front();
continue;
}
}
} else {
return Ok(children);
}
}
}
pub fn pop_value<V: FromStr>(&mut self) -> DeserializeResult<V> {
loop {
let child = self
.content
.pop_front()
.ok_or(DeserializeError::MissingValue)?;
match child {
Content::Element(_) => {
return Err(DeserializeError::UnexpectedContent(self.content.clone()))
}
Content::Text(t) => {
return Ok(
<V as FromStr>::from_str(&t).map_err(|_| DeserializeError::FromStr(t))?
)
}
Content::PI => {}
Content::Comment(_) => {}
}
}
}
pub fn pop_value_opt<V: FromStr>(&mut self) -> DeserializeResult<Option<V>> {
loop {
let child = self.content.pop_front();
if let Some(child) = child {
match child {
Content::Element(_) => {
return Err(DeserializeError::UnexpectedContent(self.content.clone()))
}
Content::Text(t) => {
return Ok(Some(
<V as FromStr>::from_str(&t)
.map_err(|_| DeserializeError::FromStr(t))?,
))
}
Content::PI => {}
Content::Comment(_) => {}
}
} else {
return Ok(None);
}
}
}
pub fn no_more_content(self) -> DeserializeResult<Self> {
if self
.content
.iter()
.filter(|content| match content {
Content::Element(_) => true,
Content::Text(_) => true,
Content::PI => false,
Content::Comment(_) => false,
})
.collect::<Vec<_>>()
.is_empty()
{
Ok(self)
} else {
Err(DeserializeError::UnexpectedContent(self.content))
}
}
pub fn builder(name: impl ToString, namespace: Option<impl ToString>) -> ElementBuilder {
ElementBuilder::new(name, namespace)
}
}
pub struct ElementBuilder {
name: Name,
namespace_declaration_overrides: Vec<NamespaceDeclaration>,
attributes: Vec<(Name, String)>,
content: Vec<ContentBuilder>,
}
impl ElementBuilder {
pub fn new(name: impl ToString, namespace: Option<impl ToString>) -> Self {
Self {
name: Name {
namespace: namespace.map(|namespace| namespace.to_string()),
local_name: name.to_string(),
},
namespace_declaration_overrides: Vec::new(),
attributes: Vec::new(),
content: Vec::new(),
}
}
pub fn push_namespace_declaration_override(
mut self,
prefix: Option<impl ToString>,
namespace: impl ToString,
) -> Self {
self.namespace_declaration_overrides
.push(NamespaceDeclaration {
prefix: prefix.map(|prefix| prefix.to_string()),
namespace: namespace.to_string(),
});
self
}
pub fn push_attribute<N: ToString, V: ToString>(mut self, name: N, value: V) -> Self {
self.attributes.push((
// TODO: make sure name is a valid name, same for prefixes
Name {
namespace: None,
local_name: name.to_string(),
},
value.to_string(),
));
self
}
pub fn push_attribute_namespaced(
mut self,
namespace: impl ToString,
name: impl ToString,
value: impl ToString,
) -> Self {
self.attributes.push((
Name {
namespace: Some(namespace.to_string()),
local_name: name.to_string(),
},
value.to_string(),
));
self
}
pub fn push_child(mut self, child: impl IntoElement) -> Self {
self.content.push(ContentBuilder::Element(child.builder()));
self
}
pub fn push_text(mut self, text: impl ToString) -> Self {
self.content.push(ContentBuilder::Text(text.to_string()));
self
}
pub fn push_attribute_opt(self, name: impl ToString, value: Option<impl ToString>) -> Self {
if let Some(value) = value {
self.push_attribute(name, value)
} else {
self
}
}
pub fn push_attribute_opt_namespaced(
self,
namespace: impl ToString,
name: impl ToString,
value: Option<impl ToString>,
) -> Self {
if let Some(value) = value {
self.push_attribute_namespaced(namespace, name, value)
} else {
self
}
}
pub fn push_child_opt(self, child: Option<impl IntoElement>) -> Self {
if let Some(child) = child {
self.push_child(child)
} else {
self
}
}
pub fn push_text_opt(self, text: Option<impl ToString>) -> Self {
if let Some(text) = text {
self.push_text(text)
} else {
self
}
}
pub fn push_content(mut self, content: ContentBuilder) -> Self {
self.content.push(content);
self
}
pub fn push_children(self, children: Vec<impl IntoContent>) -> Self {
let mut element_builder = self;
for child in children {
element_builder = element_builder.push_content(child.builder())
}
element_builder
}
pub fn build(&self) -> Result<Element> {
let mut namespace_declaration_overrides = HashSet::new();
for namespace_declaration in &self.namespace_declaration_overrides {
if !namespace_declaration_overrides.insert(namespace_declaration.clone()) {
return Err(Error::DuplicateNameSpaceDeclaration(
namespace_declaration.clone(),
));
}
}
let mut attributes = HashMap::new();
for (att_name, att_value) in &self.attributes {
if attributes
.insert(att_name.clone(), att_value.to_string())
.is_some()
{
// TODO: better error
return Err(Error::DuplicateAttribute(att_name.local_name.to_string()));
}
}
let content: Result<VecDeque<Content>> = self
.content
.iter()
.map(|content_builder| -> Result<Content> { Ok(content_builder.build()?) })
.collect();
let content = content?;
Ok(Element {
name: self.name.clone(),
namespace_declaration_overrides,
attributes,
content,
})
}
}
pub trait IntoContent {
fn into_content(&self) -> Content {
self.builder().build().unwrap()
}
fn builder(&self) -> ContentBuilder;
}
impl<T> IntoContent for T
where
T: IntoElement,
{
fn builder(&self) -> ContentBuilder {
ContentBuilder::Element(self.builder())
}
}
pub trait FromContent: Sized {
fn from_content(content: Content) -> DeserializeResult<Self>;
}
pub enum ContentBuilder {
Element(ElementBuilder),
Text(String),
Comment(String),
}
impl ContentBuilder {
pub fn build(&self) -> Result<Content> {
match self {
ContentBuilder::Element(element_builder) => {
Ok(Content::Element(element_builder.build()?))
}
ContentBuilder::Text(text) => Ok(Content::Text(text.to_string())),
ContentBuilder::Comment(s) => Ok(Content::Comment(s.to_string())),
}
}
}
pub fn escape_str(s: &str) -> String {
let mut string = String::new();
for str in s.split_inclusive(|c| c == '<' || c == '&' || c == '>') {
if let Some(str) = str.strip_suffix('<') {
if !str.is_empty() {
string.push_str(str)
}
string.push_str("<");
} else if let Some(str) = str.strip_suffix('&') {
if !str.is_empty() {
string.push_str(str)
}
string.push_str("&");
} else if let Some(str) = str.strip_suffix('>') {
if !str.is_empty() {
string.push_str(str)
}
string.push_str(">");
} else {
if !str.is_empty() {
string.push_str(str)
}
}
}
string
}
// impl<'s> TryFrom<xml::Element<'s>> for Element<'s> {
// type Error = Error;
// fn try_from(xml_element: xml::Element) -> Result<Self, Self::Error> {
// match &xml_element {
// xml::Element::Empty(empty_elem_tag) => {
// let namespace_decl;
// let attributes;
// empty_elem_tag
// .attributes
// .into_iter()
// .filter(|attribute| matches!(attribute, Attribute::NamespaceDeclaration(_)));
// todo!()
// }
// xml::Element::NotEmpty(stag, content, etag) => todo!(),
// }
// }
// }
// example of deriving an element:
// #[derive(XMLWrite, XMLRead)]
// #[peanuts(xmlns = "jabber:client", xmlns:stream = "http://etherx.jabber.org/streams", prefix = "stream")]
// pub struct Stream {
// from: JID,
// id: String,
// to: JID,
// version: String,
// #[peanuts(namespace = "http://www.w3.org/XML/1998/namespace")]
// lang: Lang,
// }
// note: if an element name has a prefix all unprefixed attributes are qualified by the namespace of the prefix, so in this example from's Name's namespace would be "http://etherx.jabber.org/streams"