From 78e8a06ec2a75159774535d182a90ad7b25b3889 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 23 Apr 2017 17:30:23 +0100 Subject: [PATCH] Add a presence parser. --- src/lib.rs | 2 + src/presence.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/presence.rs diff --git a/src/lib.rs b/src/lib.rs index 7be94346..79b7d1da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,8 @@ pub mod ns; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod body; /// XEP-0004: Data Forms diff --git a/src/presence.rs b/src/presence.rs new file mode 100644 index 00000000..51e5854e --- /dev/null +++ b/src/presence.rs @@ -0,0 +1,179 @@ +use std::str::FromStr; + +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +use delay; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum PresencePayload { + Delay(delay::Delay), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PresenceType { + /// This value is not an acceptable 'type' attribute, it is only used + /// internally to signal the absence of 'type'. + Available, + Error, + Probe, + Subscribe, + Subscribed, + Unavailable, + Unsubscribe, + Unsubscribed, +} + +impl Default for PresenceType { + fn default() -> PresenceType { + PresenceType::Available + } +} + +impl FromStr for PresenceType { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "error" => PresenceType::Error, + "probe" => PresenceType::Probe, + "subscribe" => PresenceType::Subscribe, + "subscribed" => PresenceType::Subscribed, + "unavailable" => PresenceType::Unavailable, + "unsubscribe" => PresenceType::Unsubscribe, + "unsubscribed" => PresenceType::Unsubscribed, + + _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")), + }) + } +} + +impl IntoAttributeValue for PresenceType { + fn into_attribute_value(self) -> Option { + Some(match self { + PresenceType::Available => return None, + + PresenceType::Error => "error", + PresenceType::Probe => "probe", + PresenceType::Subscribe => "subscribe", + PresenceType::Subscribed => "subscribed", + PresenceType::Unavailable => "unavailable", + PresenceType::Unsubscribe => "unsubscribe", + PresenceType::Unsubscribed => "unsubscribed", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum PresencePayloadType { + XML(Element), + Parsed(PresencePayload), +} + +#[derive(Debug, Clone)] +pub struct Presence { + pub from: Option, + pub to: Option, + pub id: Option, + pub type_: PresenceType, + pub payloads: Vec, +} + +pub fn parse_presence(root: &Element) -> Result { + if !root.is("presence", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not a presence element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_.parse()?, + None => Default::default(), + }; + let mut payloads = vec!(); + for elem in root.children() { + let payload = if let Ok(delay) = delay::parse_delay(elem) { + Some(PresencePayload::Delay(delay)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => PresencePayloadType::Parsed(payload), + None => PresencePayloadType::XML(elem.clone()), + }); + } + Ok(Presence { + from: from, + to: to, + id: id, + type_: type_, + payloads: payloads, + }) +} + +pub fn serialise_payload(payload: &PresencePayload) -> Element { + match *payload { + PresencePayload::Delay(ref delay) => delay::serialise(delay), + } +} + +pub fn serialise(presence: &Presence) -> Element { + let mut stanza = Element::builder("presence") + .ns(ns::JABBER_CLIENT) + .attr("from", presence.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", presence.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", presence.id.clone()) + .attr("type", presence.type_.clone()) + .build(); + for child in presence.payloads.clone() { + let elem = match child { + PresencePayloadType::XML(elem) => elem, + PresencePayloadType::Parsed(payload) => serialise_payload(&payload), + }; + stanza.append_child(elem); + } + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use presence; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + assert_eq!(presence.from, None); + assert_eq!(presence.to, None); + assert_eq!(presence.id, None); + assert_eq!(presence.type_, presence::PresenceType::Available); + assert!(presence.payloads.is_empty()); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let presence = presence::parse_presence(&elem).unwrap(); + let presence2 = presence::Presence { + from: None, + to: None, + id: None, + type_: presence::PresenceType::Unavailable, + payloads: vec!(), + }; + let elem2 = presence::serialise(&presence2); + assert_eq!(elem, elem2); + println!("{:#?}", presence); + } +}