// 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; } pub trait IntoElement { fn into_element(&self) -> Element; fn get_content(&self) -> Vec { 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, 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, 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, // 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, // TODO: make a hashmap maybe? to be able to address parts of the content individually pub content: Vec, } 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> for Element<'s> { // type Error = Error; // fn try_from(xml_element: xml::Element) -> Result { // 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"