summaryrefslogtreecommitdiffstats
path: root/src/stanza
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@blos.sm>2023-10-20 02:34:47 +0100
committerLibravatar cel 🌸 <cel@blos.sm>2023-10-20 02:34:47 +0100
commit2536fa4937f0283b4187142cc6cede8e1dbfafa8 (patch)
treee62edc29e91a5574e4a35b96745c9454d8bf88a1 /src/stanza
parentb4652b3939858306806d48781fb7c7b1ec52ad1d (diff)
downloadluz-2536fa4937f0283b4187142cc6cede8e1dbfafa8.tar.gz
luz-2536fa4937f0283b4187142cc6cede8e1dbfafa8.tar.bz2
luz-2536fa4937f0283b4187142cc6cede8e1dbfafa8.zip
WIP: mess
Diffstat (limited to 'src/stanza')
-rw-r--r--src/stanza/bind.rs140
-rw-r--r--src/stanza/iq.rs1
-rw-r--r--src/stanza/mod.rs132
-rw-r--r--src/stanza/sasl.rs1
4 files changed, 130 insertions, 144 deletions
diff --git a/src/stanza/bind.rs b/src/stanza/bind.rs
index 5e9704f..939a716 100644
--- a/src/stanza/bind.rs
+++ b/src/stanza/bind.rs
@@ -1,12 +1,4 @@
-use std::collections::BTreeMap;
-
-use quick_xml::{
- events::{BytesStart, BytesText, Event},
- name::QName,
- Reader,
-};
-
-use super::{Element, IntoElement, Node};
+use super::{Element, ElementParseError};
use crate::{JabberError, JID};
const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-bind";
@@ -17,120 +9,40 @@ pub struct Bind {
}
impl From<Bind> for Element {
- fn from(value: Bind) -> Self {
- let mut namespace_declarations = Box::new(BTreeMap::new());
- namespace_declarations.insert(None, XMLNS.to_owned());
- let mut children = Vec::new();
- if let Some(resource) = value.resource {
- children.push(Node::Element(
- Element { prefix: None, localname: "", namespace: , namespace_declarations: , attributes: , children: }
- )
-
- )
- }
- Self {
- prefix: None,
- localname: "bind".to_string(),
- namespace: XMLNS.to_owned(),
- namespace_declarations,
- attributes: todo!(),
- children: todo!(),
- }
- }
-}
-
-impl IntoElement for Bind {
- fn event(&self) -> quick_xml::events::Event<'static> {
- let mut bind_event = BytesStart::new("bind");
- bind_event.push_attribute(("xmlns", XMLNS));
- if self.resource.is_none() && self.jid.is_none() {
- return Event::Empty(bind_event);
- } else {
- return Event::Start(bind_event);
+ fn from(bind: Bind) -> Self {
+ let bind_element = Element::new("bind", None, XMLNS);
+ bind_element.push_namespace_declaration((None, XMLNS));
+ if let Some(resource) = bind.resource {
+ let resource_element = Element::new("resource", None, XMLNS);
+ resource_element.push_child(resource);
+ bind_element.push_child(resource_element)
}
- }
-
- fn children(&self) -> Option<Vec<Element<'static>>> {
- if let Some(resource) = &self.resource {
- let resource_event: BytesStart<'static> = BytesStart::new("resource");
- let resource_child: BytesText<'static> = BytesText::new(resource).into_owned();
- let resource_child: Element<'static> = Element {
- event: Event::Text(resource_child),
- children: None,
- };
- let resource_element: Element<'static> = Element {
- event: Event::Start(resource_event),
- children: Some(vec![resource_child]),
- };
- return Some(vec![resource_element]);
- } else if let Some(jid) = &self.jid {
- let jid_event = BytesStart::new("jid");
- let jid_child = BytesText::new(&jid.to_string()).into_owned();
- let jid_child = Element {
- event: Event::Text(jid_child),
- children: None,
- };
- let jid_element = Element {
- event: Event::Start(jid_event),
- children: Some(vec![jid_child]),
- };
- return Some(vec![jid_element]);
+ if let Some(jid) = bind.jid {
+ let jid_element = Element::new("jid", None, XMLNS);
+ jid_element.push_child(jid);
+ bind_element.push_child(jid_element)
}
- None
+ bind_element
}
}
-impl TryFrom<Element<'static>> for Bind {
+impl TryFrom<Element> for Bind {
type Error = JabberError;
- fn try_from(element: Element<'static>) -> Result<Self, Self::Error> {
- if let Event::Start(start) = &element.event {
- let buf: Vec<u8> = Vec::new();
- let reader = Reader::from_reader(buf);
- if start.name() == QName(b"bind")
- && start.try_get_attribute("xmlns")?.is_some_and(|attribute| {
- attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS
- })
- {
- let child: Element<'static> = element.child()?.clone();
- if let Event::Start(start) = &child.event {
- match start.name() {
- QName(b"resource") => {
- let resource_text = child.child()?;
- if let Event::Text(text) = &resource_text.event {
- return Ok(Self {
- resource: Some(text.unescape()?.into_owned()),
- jid: None,
- });
- }
- }
- QName(b"jid") => {
- let jid_text = child.child()?;
- if let Event::Text(text) = &jid_text.event {
- return Ok(Self {
- jid: Some(text.unescape()?.into_owned().try_into()?),
- resource: None,
- });
- }
- }
- _ => return Err(JabberError::UnexpectedElement),
- }
+ fn try_from(element: Element) -> Result<Self, Self::Error> {
+ if element.namespace() == XMLNS && element.localname() == "bind" {
+ let (resource, jid);
+ let child: &Element = element.child()?;
+ if child.namespace() == XMLNS {
+ match child.localname() {
+ "resource" => Bind::new(Some(
+ child
+ .text_content()?
+ .first()
+ .ok_or(ElementParseError::NoContent)?,
+ )),
}
}
- } else if let Event::Empty(start) = &element.event {
- let buf: Vec<u8> = Vec::new();
- let reader = Reader::from_reader(buf);
- if start.name() == QName(b"bind")
- && start.try_get_attribute("xmlns")?.is_some_and(|attribute| {
- attribute.decode_and_unescape_value(&reader).unwrap() == XMLNS
- })
- {
- return Ok(Bind {
- resource: None,
- jid: None,
- });
- }
}
- Err(JabberError::UnexpectedElement)
}
}
diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs
index 8a373b2..6c7dee3 100644
--- a/src/stanza/iq.rs
+++ b/src/stanza/iq.rs
@@ -7,7 +7,6 @@ use quick_xml::{
use crate::{JabberClient, JabberError, JID};
-use super::{Element, IntoElement};
use crate::Result;
#[derive(Debug)]
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> {
diff --git a/src/stanza/sasl.rs b/src/stanza/sasl.rs
index 50ffd83..20cd063 100644
--- a/src/stanza/sasl.rs
+++ b/src/stanza/sasl.rs
@@ -7,7 +7,6 @@ use crate::error::SASLError;
use crate::JabberError;
use super::Element;
-use super::IntoElement;
const XMLNS: &str = "urn:ietf:params:xml:ns:xmpp-sasl";