diff options
| author | 2025-03-24 20:51:15 +0000 | |
|---|---|---|
| committer | 2025-03-24 20:51:15 +0000 | |
| commit | 85a3093674506b60b31a023ae40df1d65b2f1fb4 (patch) | |
| tree | 3443a36a3bf02544440a934d387c5b85e83b2264 | |
| parent | f0cb64670a7fdc49c334d99428458dfaf450171c (diff) | |
| download | luz-85a3093674506b60b31a023ae40df1d65b2f1fb4.tar.gz luz-85a3093674506b60b31a023ae40df1d65b2f1fb4.tar.bz2 luz-85a3093674506b60b31a023ae40df1d65b2f1fb4.zip | |
feat(stanza): xep-0030
Diffstat (limited to '')
| -rw-r--r-- | stanza/Cargo.toml | 1 | ||||
| -rw-r--r-- | stanza/src/client/iq.rs | 19 | ||||
| -rw-r--r-- | stanza/src/lib.rs | 2 | ||||
| -rw-r--r-- | stanza/src/xep_0030/info.rs | 110 | ||||
| -rw-r--r-- | stanza/src/xep_0030/items.rs | 63 | ||||
| -rw-r--r-- | stanza/src/xep_0030/mod.rs | 2 | 
6 files changed, 197 insertions, 0 deletions
| diff --git a/stanza/Cargo.toml b/stanza/Cargo.toml index f21ac1e..1b6e4d0 100644 --- a/stanza/Cargo.toml +++ b/stanza/Cargo.toml @@ -11,3 +11,4 @@ chrono = { version = "0.4.40", optional = true }  [features]  xep_0203 = ["dep:chrono"] +xep_0030 = [] diff --git a/stanza/src/client/iq.rs b/stanza/src/client/iq.rs index 5c39938..5f5ccd2 100644 --- a/stanza/src/client/iq.rs +++ b/stanza/src/client/iq.rs @@ -13,6 +13,9 @@ use crate::{      xep_0199::{self, Ping},  }; +#[cfg(feature = "xep_0030")] +use crate::xep_0030::{self, info, items}; +  use super::XMLNS;  #[derive(Debug, Clone)] @@ -31,6 +34,10 @@ pub struct Iq {  #[derive(Clone, Debug)]  pub enum Query {      Bind(Bind), +    #[cfg(feature = "xep_0030")] +    DiscoInfo(info::Query), +    #[cfg(feature = "xep_0030")] +    DiscoItems(items::Query),      Ping(Ping),      Roster(roster::Query),      Unsupported, @@ -44,6 +51,14 @@ impl FromElement for Query {              (Some(roster::XMLNS), "query") => {                  Ok(Query::Roster(roster::Query::from_element(element)?))              } +            #[cfg(feature = "xep_0030")] +            (Some(xep_0030::info::XMLNS), "query") => { +                Ok(Query::DiscoInfo(info::Query::from_element(element)?)) +            } +            #[cfg(feature = "xep_0030")] +            (Some(xep_0030::items::XMLNS), "query") => { +                Ok(Query::DiscoItems(items::Query::from_element(element)?)) +            }              _ => Ok(Query::Unsupported),          }      } @@ -57,6 +72,10 @@ impl IntoElement for Query {              Query::Roster(query) => query.builder(),              // TODO: consider what to do if attempt to serialize unsupported              Query::Unsupported => todo!(), +            #[cfg(feature = "xep_0030")] +            Query::DiscoInfo(query) => query.builder(), +            #[cfg(feature = "xep_0030")] +            Query::DiscoItems(query) => query.builder(),          }      }  } diff --git a/stanza/src/lib.rs b/stanza/src/lib.rs index 4629c07..c8daaa5 100644 --- a/stanza/src/lib.rs +++ b/stanza/src/lib.rs @@ -8,6 +8,8 @@ pub mod stanza_error;  pub mod starttls;  pub mod stream;  pub mod stream_error; +#[cfg(feature = "xep_0030")] +pub mod xep_0030;  pub mod xep_0199;  #[cfg(feature = "xep_0203")]  pub mod xep_0203; diff --git a/stanza/src/xep_0030/info.rs b/stanza/src/xep_0030/info.rs new file mode 100644 index 0000000..cec2dcb --- /dev/null +++ b/stanza/src/xep_0030/info.rs @@ -0,0 +1,110 @@ +use peanuts::{ +    element::{FromElement, IntoElement}, +    DeserializeError, Element, +}; + +pub const XMLNS: &str = "http://jabber.org/protocol/disco#info"; + +#[derive(Debug, Clone)] +pub struct Query { +    node: Option<String>, +    features: Vec<Feature>, +    identities: Vec<Identity>, +} + +impl FromElement for Query { +    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { +        element.check_name("query")?; +        element.check_namespace(XMLNS)?; + +        let node = element.attribute_opt("node")?; + +        let features = element.children()?; +        let identities = element.children()?; + +        Ok(Self { +            node, +            features, +            identities, +        }) +    } +} + +impl IntoElement for Query { +    fn builder(&self) -> peanuts::element::ElementBuilder { +        Element::builder("query", Some(XMLNS)) +            .push_attribute_opt("node", self.node.clone()) +            .push_children(self.features.clone()) +            .push_children(self.identities.clone()) +    } +} + +// no children +#[derive(Debug, Clone)] +pub struct Identity { +    /// non empty string +    pub category: String, +    pub name: Option<String>, +    /// non empty string +    pub r#type: String, +} + +impl FromElement for Identity { +    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { +        element.check_name("identity")?; +        element.check_namespace(XMLNS)?; + +        let category: String = element.attribute("category")?; + +        if category.is_empty() { +            return Err(DeserializeError::AttributeEmptyString( +                "category".to_string(), +            )); +        } + +        let name = element.attribute_opt("name")?; +        let r#type: String = element.attribute("type")?; + +        if r#type.is_empty() { +            return Err(DeserializeError::AttributeEmptyString("type".to_string())); +        } + +        Ok(Self { +            category, +            name, +            r#type, +        }) +    } +} + +impl IntoElement for Identity { +    fn builder(&self) -> peanuts::element::ElementBuilder { +        Element::builder("identity", Some(XMLNS)) +            .push_attribute("category", self.category.clone()) +            .push_attribute_opt("name", self.name.clone()) +            .push_attribute("type", self.r#type.clone()) +    } +} + +// no children +#[derive(Debug, Clone)] +pub struct Feature { +    pub var: String, +} + +impl FromElement for Feature { +    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> { +        element.check_name("feature")?; +        element.check_namespace(XMLNS)?; + +        let var = element.attribute("var")?; + +        Ok(Self { var }) +    } +} + +impl IntoElement for Feature { +    fn builder(&self) -> peanuts::element::ElementBuilder { +        Element::builder("feature", Some(XMLNS)).push_attribute("var", self.var.clone()) +    } +} diff --git a/stanza/src/xep_0030/items.rs b/stanza/src/xep_0030/items.rs new file mode 100644 index 0000000..78fe332 --- /dev/null +++ b/stanza/src/xep_0030/items.rs @@ -0,0 +1,63 @@ +use jid::JID; +use peanuts::{ +    element::{FromElement, IntoElement}, +    Element, +}; + +pub const XMLNS: &str = "http://jabber.org/protocol/disco#items"; + +#[derive(Debug, Clone)] +pub struct Query { +    node: Option<String>, +    items: Vec<Item>, +} + +impl FromElement for Query { +    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { +        element.check_name("query")?; +        element.check_namespace(XMLNS)?; + +        let node = element.attribute_opt("node")?; + +        let items = element.pop_children()?; + +        Ok(Self { node, items }) +    } +} + +impl IntoElement for Query { +    fn builder(&self) -> peanuts::element::ElementBuilder { +        Element::builder("query", Some(XMLNS)) +            .push_attribute_opt("node", self.node.clone()) +            .push_children(self.items.clone()) +    } +} + +#[derive(Debug, Clone)] +pub struct Item { +    jid: JID, +    name: Option<String>, +    node: Option<String>, +} + +impl FromElement for Item { +    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> { +        element.check_name("item")?; +        element.check_namespace(XMLNS)?; + +        let jid = element.attribute("jid")?; +        let name = element.attribute_opt("name")?; +        let node = element.attribute_opt("node")?; + +        Ok(Self { jid, name, node }) +    } +} + +impl IntoElement for Item { +    fn builder(&self) -> peanuts::element::ElementBuilder { +        Element::builder("item", Some(XMLNS)) +            .push_attribute("jid", self.jid.clone()) +            .push_attribute_opt("name", self.name.clone()) +            .push_attribute_opt("node", self.node.clone()) +    } +} diff --git a/stanza/src/xep_0030/mod.rs b/stanza/src/xep_0030/mod.rs new file mode 100644 index 0000000..914c17b --- /dev/null +++ b/stanza/src/xep_0030/mod.rs @@ -0,0 +1,2 @@ +pub mod info; +pub mod items; | 
