diff options
Diffstat (limited to '')
-rw-r--r-- | src/stanza/mod.rs | 132 |
1 files changed, 104 insertions, 28 deletions
diff --git a/src/stanza/mod.rs b/src/stanza/mod.rs index 1bb3fc2..13fc31e 100644 --- a/src/stanza/mod.rs +++ b/src/stanza/mod.rs @@ -17,8 +17,6 @@ use tokio::io::{AsyncBufRead, AsyncWrite}; use crate::{JabberError, Result}; -pub type Prefix<'s> = Option<&'s str>; - #[derive(Clone, Debug)] /// represents an xml element as a tree of nodes pub struct Element { @@ -78,19 +76,6 @@ pub struct Element { children: Box<Vec<Node>>, } -impl Element { - pub fn new_empty<S: ToString, C: Into<Node>>( - prefix: Option<S>, - localname: S, - namespace: S, - namespace_declarations: BTreeMap<Option<S>, S>, - attributes: BTreeMap<S, S>, - children: Vec<C>, - ) { - } - pub fn push_child<C: Into<Node>>(&mut self, node: C) {} -} - #[derive(Clone, Debug)] pub enum Node { Element(Element), @@ -244,22 +229,22 @@ impl Element { /// by a parent, or itself. fn namespace_qualified<S: AsRef<str>>( &self, - local_namespaces: &BTreeMap<Option<S>, S>, + namespace_context: &BTreeMap<Option<S>, S>, ) -> bool { + // create a new local_namespaces combining that in the context and those declared within the element + let mut local_namespaces = *namespace_context.clone(); + self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration)); + if let Some(namespace) = local_namespaces.get(self.prefix) { if namespace != self.namespace { return false; } + } else { + return false; }; - if let Some(namespace) = self.namespace_declarations.get(&self.prefix) { - if namespace != self.namespace { - return false; - } - } - for child in *self.children { - if child.namespace_qualified(local_namespaces) == false { + if child.namespace_qualified(&local_namespaces) == false { return false; } } @@ -267,6 +252,7 @@ impl Element { true } + /// writes an element to a writer. the element's namespace must be qualified by the /// context given in `local_namespaces` or the element's internal namespace declarations pub async fn write<S: AsRef<str>, W: AsyncWrite + Unpin + Send>( @@ -274,7 +260,7 @@ impl Element { writer: &mut Writer<W>, local_namespaces: &BTreeMap<Option<S>, S>, ) -> Result<()> { - // TODO: NEXT: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s + // TODO: instead of having a check if namespace qualified function, have the namespace declarations be added if needed given the context when converting from `Element` to `Event`s if self.namespace_qualified(local_namespaces) { let events: Vec<Event> = self.into(); for event in events { @@ -553,17 +539,107 @@ impl Node { fn namespace_qualified<S: AsRef<str>>( &self, - local_namespaces: &BTreeMap<Option<S>, S>, + namespace_context: &BTreeMap<Option<S>, S>, ) -> bool { match self { - Self::Element(e) => e.namespace_qualified(local_namespaces), + Self::Element(e) => e.namespace_qualified(namespace_context), _ => true, } } } -// the issue is i don't know how to validate that an element always has a namespace when it is being written -// TODO: ElementBuilder that makes it easier to build an element under a namespace +pub enum NodeBuilder { + Text(String), + Element(ElementBuilder), +} + +pub struct ElementBuilder { + localname: String, + prefix: Option<String>, + namespace: String, + namespace_declarations: BTreeMap<Option<String>, String>, + attributes: BTreeMap<String, String>, + children: Vec<NodeBuilder>, +} + +impl ElementBuilder { + pub fn new<S: ToString>(localname: S, prefix: Option<S>, namespace: S) -> Self { + Self { + prefix, + localname, + namespace, + namespace_declarations: Box::new(BTreeMap::new()), + attributes: Box::new(BTreeMap::new()), + children: Box::new(Vec::new()), + } + } + + pub fn push_namespace_declaration<S: ToString>( + &mut self, + (prefix, namespace): (Option<S>, S), + ) -> Option<S> { + self.namespace_declarations.insert(prefix, namespace) + } + + pub fn push_attribute<S: ToString>(&mut self, (key, value): (S, S)) -> Option<S> { + self.attributes.insert(key, value) + } + + pub fn push_child(&mut self, child: Node) { + self.children.push(child) + } + + /// checks if there is a namespace conflict within the element being built + pub fn namespace_conflict<S: AsRef<str>>( + &self + ) -> bool { + self.namespace_conflict_recursive(&BTreeMap::new()) + } + + fn namespace_conflict_recursive<S: AsRef<str>>( + &self, + parent_namespaces: &BTreeMap<Option<S>, S>, + ) -> bool { + // create a new local_namespaces combining that in the context and those declared within the element + let mut local_namespaces = *parent_namespaces.clone(); + self.namespace_declarations.iter().for_each(|prefix, declaration| local_namespaces.insert(prefix, declaration)); + + if let Some(namespace) = local_namespaces.get(self.prefix) { + if namespace != self.namespace { + return false; + } + } else { + return false; + }; + + for child in *self.children { + if child.namespace_conflict(&local_namespaces) == false { + return false; + } + } + + true + } + + // check for possible conflicts in namespace + pub fn build(self) -> Result<Element> { + for child in self.children { + match child { + Node::Element(e) => { + if !e.namespace_conflict() + } + } + } + Element { + prefix: self.prefix, + localname: self.localname, + namespace: self.namespace, + namespace_declarations: self.namespace_declarations, + attributes: self.attributes, + children: self.children, + } + } +} #[derive(Debug)] pub enum ElementError<'e> { |