aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2024-12-22 20:40:59 +0000
committerLibravatar cel 🌸 <cel@bunny.garden>2024-12-22 20:40:59 +0000
commit89351e956368ad3112127570ef03dd2547730ce5 (patch)
tree457143801922e23c9e72a7d59e7ca8e6b24a1c9b
parent27f90bd85f2abf2ecdac69880801d657f125b6ce (diff)
downloadluz-89351e956368ad3112127570ef03dd2547730ce5.tar.gz
luz-89351e956368ad3112127570ef03dd2547730ce5.tar.bz2
luz-89351e956368ad3112127570ef03dd2547730ce5.zip
add jabber:iq:stanza parsers
-rw-r--r--stanza/src/lib.rs1
-rw-r--r--stanza/src/roster.rs153
2 files changed, 154 insertions, 0 deletions
diff --git a/stanza/src/lib.rs b/stanza/src/lib.rs
index f3b0dca..4120207 100644
--- a/stanza/src/lib.rs
+++ b/stanza/src/lib.rs
@@ -2,6 +2,7 @@ use peanuts::declaration::VersionInfo;
pub mod bind;
pub mod client;
+pub mod roster;
pub mod sasl;
pub mod stanza_error;
pub mod starttls;
diff --git a/stanza/src/roster.rs b/stanza/src/roster.rs
new file mode 100644
index 0000000..b49fcc3
--- /dev/null
+++ b/stanza/src/roster.rs
@@ -0,0 +1,153 @@
+use std::str::FromStr;
+
+use jid::JID;
+use peanuts::{
+ element::{FromElement, IntoElement},
+ DeserializeError, Element,
+};
+
+pub const XMLNS: &str = "jabber:iq:roster";
+
+pub struct Query {
+ ver: 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 ver = element.attribute_opt("ver")?;
+ let items = element.pop_children()?;
+
+ Ok(Self { ver, items })
+ }
+}
+
+impl IntoElement for Query {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("query", Some(XMLNS))
+ .push_attribute_opt("ver", self.ver.clone())
+ .push_children(self.items.clone())
+ }
+}
+
+#[derive(Clone)]
+pub struct Item {
+ approved: Option<bool>,
+ ask: bool,
+ jid: JID,
+ name: Option<String>,
+ subscription: Subscription,
+ groups: Vec<Group>,
+}
+
+impl FromElement for Item {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("item")?;
+ element.check_namespace(XMLNS)?;
+
+ let approved = element.attribute_opt("approved")?;
+ let ask = if let Some(result) = element.attribute_opt("ask")?.map(|v| {
+ if v == "subscribe" {
+ Ok(true)
+ } else {
+ Err(DeserializeError::FromStr(v))
+ }
+ }) {
+ result?
+ } else {
+ false
+ };
+ let jid = element.attribute("jid")?;
+ let name = element.attribute_opt("name")?;
+ let subscription = element.attribute_opt("subscription")?.unwrap_or_default();
+ let groups = element.pop_children()?;
+
+ Ok(Self {
+ approved,
+ ask,
+ jid,
+ name,
+ subscription,
+ groups,
+ })
+ }
+}
+
+impl IntoElement for Item {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("item", Some(XMLNS))
+ .push_attribute_opt("approved", self.approved)
+ .push_attribute_opt(
+ "ask",
+ if self.ask {
+ Some("subscribe".to_string())
+ } else {
+ None
+ },
+ )
+ .push_attribute("jid", self.jid.clone())
+ .push_attribute_opt("name", self.name.clone())
+ .push_attribute("subscription", self.subscription)
+ .push_children(self.groups.clone())
+ }
+}
+
+#[derive(Default, Clone, Copy)]
+pub enum Subscription {
+ Both,
+ From,
+ #[default]
+ None,
+ Remove,
+ To,
+}
+
+impl FromStr for Subscription {
+ type Err = DeserializeError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "both" => Ok(Self::Both),
+ "from" => Ok(Self::From),
+ "none" => Ok(Self::None),
+ "remove" => Ok(Self::Remove),
+ "to" => Ok(Self::To),
+ s => Err(DeserializeError::FromStr(s.to_string())),
+ }
+ }
+}
+
+impl ToString for Subscription {
+ fn to_string(&self) -> String {
+ match self {
+ Subscription::Both => "both".to_string(),
+ Subscription::From => "from".to_string(),
+ Subscription::None => "none".to_string(),
+ Subscription::Remove => "remove".to_string(),
+ Subscription::To => "to".to_string(),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct Group(Option<String>);
+
+impl FromElement for Group {
+ fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("group")?;
+ element.check_namespace(XMLNS)?;
+
+ let group = element.pop_value_opt()?;
+
+ Ok(Self(group))
+ }
+}
+
+impl IntoElement for Group {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("group", Some(XMLNS)).push_text_opt(self.0.clone())
+ }
+}