WIP: refactor Element type
This commit is contained in:
parent
2a7b8834d7
commit
8ab4a8b436
|
@ -25,6 +25,7 @@ pub enum JabberError {
|
|||
ParseError,
|
||||
UnexpectedEnd,
|
||||
UnexpectedElement,
|
||||
UnexpectedText,
|
||||
XML(quick_xml::Error),
|
||||
SASL(SASLError),
|
||||
Element(ElementError<'static>),
|
||||
|
|
|
@ -6,14 +6,15 @@ pub mod sasl;
|
|||
pub mod stream;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str;
|
||||
|
||||
// const DECLARATION: BytesDecl<'_> = BytesDecl::new("1.0", None, None);
|
||||
use async_recursion::async_recursion;
|
||||
use quick_xml::events::{BytesStart, Event};
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
||||
use quick_xml::{Reader, Writer};
|
||||
use tokio::io::{AsyncBufRead, AsyncWrite};
|
||||
|
||||
use crate::JabberError;
|
||||
use crate::{JabberError, Result};
|
||||
|
||||
// #[derive(Clone, Debug)]
|
||||
// pub struct EventTree<'e> {
|
||||
|
@ -29,6 +30,9 @@ pub struct Element<'s> {
|
|||
/// element prefix
|
||||
/// e.g. `foo` in `<foo:bar />`.
|
||||
prefix: Option<&'s str>,
|
||||
/// element name
|
||||
/// e.g. `bar` in `<foo:bar />`.
|
||||
localname: &'s str,
|
||||
/// qualifying namespace
|
||||
/// an element must be qualified by a namespace
|
||||
/// e.g. for `<stream:features>` in
|
||||
|
@ -57,9 +61,6 @@ pub struct Element<'s> {
|
|||
/// ```
|
||||
/// would be `"jabber:client"`
|
||||
namespace: &'s str,
|
||||
/// element name
|
||||
/// e.g. `bar` in `<foo:bar />`.
|
||||
name: &'s str,
|
||||
/// all namespaces applied to element
|
||||
/// e.g. for `<bind>` in
|
||||
/// ```
|
||||
|
@ -73,12 +74,11 @@ pub struct Element<'s> {
|
|||
/// ```
|
||||
/// would be `[(None, "urn:ietf:params:xml:ns:xmpp-bind")]` despite
|
||||
/// `(Some("stream"), "http://etherx.jabber.org/streams")` also being available
|
||||
namespaces: Box<BTreeMap<Option<&'s str>, &'s str>>,
|
||||
namespace_declarations: Box<BTreeMap<Option<&'s str>, &'s str>>,
|
||||
/// element attributes
|
||||
attributes: Box<BTreeMap<&'s str, &'s str>>,
|
||||
// children elements namespaces contain their parents' namespaces
|
||||
///
|
||||
children: Option<Box<Vec<Node<'s>>>>,
|
||||
children: Box<Vec<Node<'s>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -87,190 +87,281 @@ pub enum Node<'s> {
|
|||
Text(&'s str),
|
||||
}
|
||||
|
||||
impl<'s> From<&Element<'s>> for Event<'s> {
|
||||
fn from(element: &Element<'s>) -> Self {
|
||||
let event;
|
||||
if let Some(prefix) = element.prefix {
|
||||
event = BytesStart::new(format!("{}:{}", prefix, element.name));
|
||||
} else {
|
||||
event = BytesStart::new(element.name);
|
||||
}
|
||||
|
||||
event
|
||||
|
||||
let event = event.with_attributes(element.attributes.into_iter());
|
||||
|
||||
match element.children.is_none() {
|
||||
true => return Event::Empty(event),
|
||||
false => return Event::Start(event),
|
||||
impl<'s> From<&Node<'s>> for Vec<Event<'s>> {
|
||||
fn from(node: &Node<'s>) -> Self {
|
||||
match node {
|
||||
Node::Element(e) => e.into(),
|
||||
Node::Text(t) => vec![Event::Text(BytesText::new(t))],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Element<'s> {
|
||||
/// returns the fully qualified name
|
||||
/// e.g. `foo:bar` in
|
||||
/// `<foo:bar>`.
|
||||
pub fn name(&self) -> &str {
|
||||
if let Some(prefix) = self.prefix {
|
||||
format!("{}:{}", prefix, self.localname).as_str()
|
||||
} else {
|
||||
self.localname
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the localname.
|
||||
/// e.g. `bar` in `<foo:bar>`
|
||||
pub fn localname(&self) -> &str {
|
||||
self.localname
|
||||
}
|
||||
|
||||
/// returns the prefix.
|
||||
/// e.g. `foo` in `<foo:bar>`. returns None if there is
|
||||
/// no prefix.
|
||||
pub fn prefix(&self) -> Option<&str> {
|
||||
self.prefix
|
||||
}
|
||||
|
||||
/// returns the namespace which applies to the current element, e.g. for
|
||||
/// `<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>`
|
||||
/// it will be `http://etherx.jabber.org/streams` but for
|
||||
/// `<stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>`
|
||||
/// it will be `jabber:client`.
|
||||
pub fn get_namespace(&self) -> &str {
|
||||
/// `<element xmlns='foo' xmlns:bar='bar'>`
|
||||
/// it will be `foo` but for
|
||||
/// `<bar:element xmlns='foo' xmlns:bar='bar'>`
|
||||
/// it will be `bar`.
|
||||
pub fn namespace(&self) -> &str {
|
||||
self.namespace
|
||||
}
|
||||
}
|
||||
|
||||
impl<'e: 'async_recursion, 'async_recursion> Element<'e> {
|
||||
pub fn write<'life0, W: AsyncWrite + Unpin + Send>(
|
||||
&'async_recursion self,
|
||||
writer: &'life0 mut Writer<W>,
|
||||
) -> ::core::pin::Pin<
|
||||
Box<
|
||||
dyn ::core::future::Future<Output = Result<(), JabberError>>
|
||||
+ 'async_recursion
|
||||
+ ::core::marker::Send,
|
||||
>,
|
||||
>
|
||||
where
|
||||
W: 'async_recursion,
|
||||
'life0: 'async_recursion,
|
||||
{
|
||||
Box::pin(async move {
|
||||
match &self.children.is_empty() {
|
||||
true => {}
|
||||
impl<'s> From<&Element<'s>> for Vec<Event<'s>> {
|
||||
fn from(element: &Element<'s>) -> Self {
|
||||
let name = element.name();
|
||||
|
||||
let event = BytesStart::new(name);
|
||||
|
||||
// namespace declarations
|
||||
let namespace_declarations = element.namespace_declarations.iter().map(|declaration| {
|
||||
let (prefix, namespace) = declaration;
|
||||
match prefix {
|
||||
Some(prefix) => return (format!("xmlns:{}", prefix).as_str(), *namespace),
|
||||
None => return ("xmlns", *namespace),
|
||||
}
|
||||
match &self.event {
|
||||
Event::Start(e) => {
|
||||
writer.write_event_async(Event::Start(e.clone())).await?;
|
||||
if let Some(children) = &self.children {
|
||||
for e in children {
|
||||
e.write(writer).await?;
|
||||
}
|
||||
}
|
||||
writer.write_event_async(Event::End(e.to_end())).await?;
|
||||
return Ok(());
|
||||
});
|
||||
let event = event.with_attributes(namespace_declarations);
|
||||
|
||||
// attributes
|
||||
let event = event.with_attributes(element.attributes.into_iter());
|
||||
|
||||
match element.children.is_empty() {
|
||||
true => return vec![Event::Empty(event)],
|
||||
false => {
|
||||
return {
|
||||
let start: Vec<Event<'s>> = vec![Event::Start(event)];
|
||||
let start_and_content: Vec<Event<'s>> = start
|
||||
.into_iter()
|
||||
.chain({
|
||||
let u = element.children.iter().fold(
|
||||
Vec::new(),
|
||||
|acc: Vec<Event<'s>>, child: &Node<'s>| {
|
||||
acc.into_iter()
|
||||
.chain(Into::<Vec<Event<'s>>>::into(child).into_iter())
|
||||
.collect()
|
||||
},
|
||||
);
|
||||
u
|
||||
})
|
||||
.collect();
|
||||
let full: Vec<Event<'s>> = start_and_content
|
||||
.into_iter()
|
||||
.chain(vec![Event::End(BytesEnd::new(name))])
|
||||
.collect();
|
||||
full
|
||||
}
|
||||
e => Ok(writer.write_event_async(e).await?),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'e> Element<'e> {
|
||||
impl<'s> Element<'s> {
|
||||
/// if there is only one child in the vec of children, will return that element
|
||||
pub fn child(&self) -> Result<&Element<'s>> {
|
||||
if self.children.len() == 1 {
|
||||
Ok(&self.children[0])
|
||||
} else {
|
||||
Err(ElementError::NoChildren.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// returns reference to children
|
||||
pub fn children(&self) -> Result<&Vec<Element<'s>>> {
|
||||
if !self.children.is_empty() {
|
||||
Ok(&self.children)
|
||||
} else {
|
||||
Err(ElementError::NoChildren.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// returns text content, error if there is none
|
||||
pub fn content(&self) -> Result<&str> {
|
||||
for node in *self.children {
|
||||
match node {
|
||||
Node::Text(t) => return Ok(t),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(ElementError::NotText)
|
||||
}
|
||||
|
||||
pub async fn write<W: AsyncWrite + Unpin + Send>(&self, writer: &mut Writer<W>) -> Result<()> {
|
||||
let events: Vec<Event> = self.into();
|
||||
for event in events {
|
||||
writer.write_event_async(event).await?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_start<W: AsyncWrite + Unpin + Send>(
|
||||
&self,
|
||||
writer: &mut Writer<W>,
|
||||
) -> Result<(), JabberError> {
|
||||
match self.event.as_ref() {
|
||||
Event::Start(e) => Ok(writer.write_event_async(Event::Start(e.clone())).await?),
|
||||
e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
|
||||
}
|
||||
) -> Result<()> {
|
||||
let mut event = BytesStart::new(self.name());
|
||||
|
||||
// namespace declarations
|
||||
self.namespace_declarations.iter().for_each(|declaration| {
|
||||
let (prefix, namespace) = declaration;
|
||||
match prefix {
|
||||
Some(prefix) => {
|
||||
event.push_attribute((format!("xmlns:{}", prefix).as_str(), *namespace))
|
||||
}
|
||||
None => event.push_attribute(("xmlns", *namespace)),
|
||||
}
|
||||
});
|
||||
|
||||
// attributes
|
||||
let event =
|
||||
event.with_attributes(self.attributes.iter().map(|(attr, value)| (*attr, *value)));
|
||||
|
||||
writer.write_event_async(Event::Start(event)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_end<W: AsyncWrite + Unpin + Send>(
|
||||
&self,
|
||||
writer: &mut Writer<W>,
|
||||
) -> Result<(), JabberError> {
|
||||
match self.event.as_ref() {
|
||||
Event::Start(e) => Ok(writer
|
||||
.write_event_async(Event::End(e.clone().to_end()))
|
||||
.await?),
|
||||
e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
|
||||
}
|
||||
) -> Result<()> {
|
||||
let event = BytesEnd::new(self.name());
|
||||
writer.write_event_async(Event::End(event)).await?;
|
||||
Ok(())
|
||||
}
|
||||
// pub async fn write_start<W: AsyncWrite + Unpin + Send>(
|
||||
// &self,
|
||||
// writer: &mut Writer<W>,
|
||||
// ) -> Result<(), JabberError> {
|
||||
// match self.event.as_ref() {
|
||||
// Event::Start(e) => Ok(writer.write_event_async(Event::Start(e.clone())).await?),
|
||||
// e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub async fn write_end<W: AsyncWrite + Unpin + Send>(
|
||||
// &self,
|
||||
// writer: &mut Writer<W>,
|
||||
// ) -> Result<(), JabberError> {
|
||||
// match self.event.as_ref() {
|
||||
// Event::Start(e) => Ok(writer
|
||||
// .write_event_async(Event::End(e.clone().to_end()))
|
||||
// .await?),
|
||||
// e => Err(ElementError::NotAStart(e.clone().into_owned()).into()),
|
||||
// }
|
||||
// }
|
||||
|
||||
#[async_recursion]
|
||||
pub async fn read<R: AsyncBufRead + Unpin + Send>(
|
||||
reader: &mut Reader<R>,
|
||||
) -> Result<Self, JabberError> {
|
||||
let element = Self::read_recursive(reader)
|
||||
local_namespaces: BTreeMap<Option<&str>, &str>,
|
||||
) -> Result<Self> {
|
||||
let node = Node::read_recursive(reader, local_namespaces)
|
||||
.await?
|
||||
.ok_or(JabberError::UnexpectedEnd);
|
||||
element
|
||||
.ok_or(JabberError::UnexpectedEnd)?;
|
||||
match node {
|
||||
Node::Element(e) => Ok(e),
|
||||
Node::Text(_) => Err(JabberError::UnexpectedText),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Node<'s> {
|
||||
#[async_recursion]
|
||||
async fn read_recursive<R: AsyncBufRead + Unpin + Send>(
|
||||
reader: &mut Reader<R>,
|
||||
) -> Result<Option<Self>, JabberError> {
|
||||
local_namespaces: BTreeMap<Option<&str>, &str>,
|
||||
) -> Result<Option<Node<'s>>> {
|
||||
let mut buf = Vec::new();
|
||||
let event = reader.read_event_into_async(&mut buf).await?;
|
||||
match event {
|
||||
Event::Start(e) => {
|
||||
let mut children_vec = Vec::new();
|
||||
let prefix = e
|
||||
.name()
|
||||
.prefix()
|
||||
.map(|prefix| str::from_utf8(prefix.into_inner())?);
|
||||
|
||||
let mut children_vec: Vec = Vec::new();
|
||||
while let Some(sub_element) = Element::read_recursive(reader).await? {
|
||||
children_vec.push(sub_element)
|
||||
}
|
||||
let mut children = None;
|
||||
if !children_vec.is_empty() {
|
||||
children = Some(children_vec)
|
||||
}
|
||||
Ok(Some(Self {
|
||||
event: Event::Start(e.into_owned()),
|
||||
children,
|
||||
}))
|
||||
Ok(Some(Self::Element(Element {
|
||||
prefix,
|
||||
localname: e.local_name().into_inner(),
|
||||
namespace: todo!(),
|
||||
namespace_declarations: todo!(),
|
||||
attributes: todo!(),
|
||||
children: todo!(),
|
||||
})))
|
||||
}
|
||||
Event::End(_) => Ok(None),
|
||||
Event::Text(e) => Ok(Some(Self::Text(e.unescape()?.as_ref()))),
|
||||
e => Ok(Some(Self {
|
||||
event: e.into_owned(),
|
||||
children: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
pub async fn read_start<R: AsyncBufRead + Unpin + Send>(
|
||||
reader: &mut Reader<R>,
|
||||
) -> Result<Self, JabberError> {
|
||||
let mut buf = Vec::new();
|
||||
let event = reader.read_event_into_async(&mut buf).await?;
|
||||
match event {
|
||||
Event::Start(e) => {
|
||||
return Ok(Self {
|
||||
event: Event::Start(e.into_owned()),
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
e => Err(ElementError::NotAStart(e.into_owned()).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// if there is only one child in the vec of children, will return that element
|
||||
pub fn child<'p>(&'p self) -> Result<&'p Element<'e>, ElementError<'static>> {
|
||||
if let Some(children) = &self.children {
|
||||
if children.len() == 1 {
|
||||
return Ok(&children[0]);
|
||||
} else {
|
||||
return Err(ElementError::MultipleChildren);
|
||||
}
|
||||
}
|
||||
Err(ElementError::NoChildren)
|
||||
}
|
||||
|
||||
/// returns reference to children
|
||||
pub fn children<'p>(&'p self) -> Result<&'p Vec<Element<'e>>, ElementError<'e>> {
|
||||
if let Some(children) = &self.children {
|
||||
return Ok(children);
|
||||
}
|
||||
Err(ElementError::NoChildren)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoElement<'e> {
|
||||
fn event(&self) -> Event<'e>;
|
||||
fn children(&self) -> Option<Vec<Element<'e>>>;
|
||||
}
|
||||
// #[async_recursion]
|
||||
// pub async fn read_start<R: AsyncBufRead + Unpin + Send>(
|
||||
// reader: &mut Reader<R>,
|
||||
// ) -> Result<Self, JabberError> {
|
||||
// let mut buf = Vec::new();
|
||||
// let event = reader.read_event_into_async(&mut buf).await?;
|
||||
// match event {
|
||||
// Event::Start(e) => {
|
||||
// return Ok(Self {
|
||||
// event: Event::Start(e.into_owned()),
|
||||
// children: None,
|
||||
// })
|
||||
// }
|
||||
// e => Err(ElementError::NotAStart(e.into_owned()).into()),
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'e, T: IntoElement<'e>> From<T> for Element<'e> {
|
||||
fn from(value: T) -> Self {
|
||||
Element {
|
||||
event: value.event(),
|
||||
children: value.children(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub trait IntoElement<'e> {
|
||||
// fn event(&self) -> Event<'e>;
|
||||
// fn children(&self) -> Option<Vec<Element<'e>>>;
|
||||
// }
|
||||
|
||||
// impl<'e, T: IntoElement<'e>> From<T> for Element<'e> {
|
||||
// fn from(value: T) -> Self {
|
||||
// Element {
|
||||
// event: value.event(),
|
||||
// children: value.children(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ElementError<'e> {
|
||||
NotAStart(Event<'e>),
|
||||
NotText,
|
||||
NoChildren,
|
||||
MultipleChildren,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue