// elements resemble a final tree, including inherited namespace information
use std::{
collections::{HashMap, HashSet},
convert::Infallible,
str::FromStr,
};
use crate::{
error::Error,
xml::{self, parsers_complete::Parser, Attribute},
Result,
};
pub trait FromElement: Sized {
fn from_element(element: Element) -> Result<Self>;
}
pub trait IntoElement {
fn into_element(&self) -> Element;
fn get_content(&self) -> Vec<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: Vec<Content>,
}
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"