aboutsummaryrefslogblamecommitdiffstats
path: root/stanza/src/xep_0060/event.rs
blob: 4ef5a6c5dec927b53049b31e8b33ac12a2de61c7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                        

                                  
















































































































































































































                                                                                                  

                         






































                                                                                             


                                  






























                                                                                        


                                


                              





                                                                                               




                                                           




                                                                                   














































































































































                                                                                         
use std::str::FromStr;

use chrono::{DateTime, Utc};
use jid::JID;
use peanuts::{
    element::{FromElement, IntoElement},
    DeserializeError, Element,
};

use crate::xep_0004::X;
#[cfg(feature = "xep_0172")]
use crate::xep_0172::{self, Nick};

pub const XMLNS: &str = "http://jabber.org/protocol/pubsub#event";

#[derive(Clone, Debug)]
pub enum Event {
    Collection(Collection),
    Configuration(Configuration),
    Delete(Delete),
    Items(Items),
    Purge(Purge),
    Subscription(Subscription),
}

impl FromElement for Event {
    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("event")?;
        element.check_namespace(XMLNS)?;

        let child = element.pop_child_one::<Element>()?;

        match child.identify() {
            (Some(XMLNS), "collection") => Ok(Self::Collection(Collection::from_element(child)?)),
            (Some(XMLNS), "configuration") => {
                Ok(Self::Configuration(Configuration::from_element(child)?))
            }
            (Some(XMLNS), "delete") => Ok(Self::Delete(Delete::from_element(child)?)),
            (Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(child)?)),
            (Some(XMLNS), "purge") => Ok(Self::Purge(Purge::from_element(child)?)),
            (Some(XMLNS), "subscription") => {
                Ok(Self::Subscription(Subscription::from_element(child)?))
            }
            _ => Err(peanuts::DeserializeError::UnexpectedElement(child)),
        }
    }
}

impl IntoElement for Event {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        let builder = Element::builder("event", Some(XMLNS));

        match self {
            Event::Collection(collection) => builder.push_child(collection.clone()),
            Event::Configuration(configuration) => builder.push_child(configuration.clone()),
            Event::Delete(delete) => builder.push_child(delete.clone()),
            Event::Items(items) => builder.push_child(items.clone()),
            Event::Purge(purge) => builder.push_child(purge.clone()),
            Event::Subscription(subscription) => builder.push_child(subscription.clone()),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Collection {
    node: Option<String>,
    r#type: CollectionType,
}

impl FromElement for Collection {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("node")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute_opt("node")?;
        let r#type = element.pop_child_one()?;

        Ok(Self { node, r#type })
    }
}

impl IntoElement for Collection {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("collection", Some(XMLNS))
            .push_attribute_opt("node", self.node.clone())
            .push_child(self.r#type.clone())
    }
}

#[derive(Clone, Debug)]
pub enum CollectionType {
    Associate(Associate),
    Disassociate(Disassociate),
}

impl FromElement for CollectionType {
    fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
        match element.identify() {
            (Some(XMLNS), "associate") => {
                Ok(CollectionType::Associate(Associate::from_element(element)?))
            }
            (Some(XMLNS), "disassociate") => Ok(CollectionType::Disassociate(
                Disassociate::from_element(element)?,
            )),
            _ => Err(DeserializeError::UnexpectedElement(element)),
        }
    }
}

impl IntoElement for CollectionType {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        match self {
            CollectionType::Associate(associate) => associate.builder(),
            CollectionType::Disassociate(disassociate) => disassociate.builder(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Associate {
    node: String,
}

impl FromElement for Associate {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("associate")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute("node")?;

        Ok(Self { node })
    }
}

impl IntoElement for Associate {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("associate", Some(XMLNS)).push_attribute("node", self.node.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Disassociate {
    node: String,
}

impl FromElement for Disassociate {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("disassociate")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute("node")?;

        Ok(Self { node })
    }
}

impl IntoElement for Disassociate {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("disassociate", Some(XMLNS)).push_attribute("node", self.node.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Configuration {
    node: Option<String>,
    configuration: Option<X>,
}

impl FromElement for Configuration {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("configuration")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute_opt("node")?;

        let configuration = element.pop_child_opt()?;

        Ok(Self {
            node,
            configuration,
        })
    }
}

impl IntoElement for Configuration {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("configuration", Some(XMLNS))
            .push_attribute_opt("node", self.node.clone())
            .push_child_opt(self.configuration.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Delete {
    node: String,
    redirect: Option<Redirect>,
}

impl FromElement for Delete {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("delete")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute("node")?;

        let redirect = element.pop_child_opt()?;

        Ok(Self { node, redirect })
    }
}

impl IntoElement for Delete {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("delete", Some(XMLNS))
            .push_attribute("node", self.node.clone())
            .push_child_opt(self.redirect.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Items {
    pub node: String,
    pub items: ItemsType,
}

impl FromElement for Items {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("items")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute("node")?;

        let items = if let Ok(items) = element.pop_children() {
            ItemsType::Item(items)
        } else {
            ItemsType::Retract(element.pop_children()?)
        };

        Ok(Self { node, items })
    }
}

impl IntoElement for Items {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        let builder =
            Element::builder("items", Some(XMLNS)).push_attribute("node", self.node.clone());

        match &self.items {
            ItemsType::Item(items) => builder.push_children(items.clone()),
            ItemsType::Retract(retracts) => builder.push_children(retracts.clone()),
        }
    }
}

#[derive(Clone, Debug)]
pub enum ItemsType {
    Item(Vec<Item>),
    Retract(Vec<Retract>),
}

#[derive(Clone, Debug)]
pub struct Item {
    pub id: Option<String>,
    pub publisher: Option<String>,
    pub item: Option<Content>,
}

impl FromElement for Item {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("item")?;
        element.check_namespace(XMLNS)?;

        let id = element.attribute_opt("id")?;
        let publisher = element.attribute_opt("publisher")?;

        let item = element.pop_child_opt()?;

        Ok(Self {
            id,
            publisher,
            item,
        })
    }
}

impl IntoElement for Item {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("item", Some(XMLNS))
            .push_attribute_opt("id", self.id.clone())
            .push_attribute_opt("publisher", self.publisher.clone())
            .push_child_opt(self.item.clone())
    }
}

#[derive(Clone, Debug)]
pub enum Content {
    #[cfg(feature = "xep_0172")]
    Nick(Nick),
    Unknown(Element),
}

impl FromElement for Content {
    fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
        match element.identify() {
            #[cfg(feature = "xep_0172")]
            (Some(xep_0172::XMLNS), "nick") => Ok(Content::Nick(Nick::from_element(element)?)),
            _ => Ok(Self::Unknown(element)),
        }
    }
}

impl IntoElement for Content {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        match self {
            #[cfg(feature = "xep_0172")]
            Content::Nick(nick) => nick.builder(),
            Content::Unknown(_e) => panic!("unknown content cannot be serialized"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Purge {
    node: String,
}

impl FromElement for Purge {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("purge")?;
        element.check_namespace(XMLNS)?;

        let node = element.attribute("node")?;

        Ok(Self { node })
    }
}

impl IntoElement for Purge {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("purge", Some(XMLNS)).push_attribute("node", self.node.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Redirect {
    uri: String,
}

impl FromElement for Redirect {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("redirect")?;
        element.check_namespace(XMLNS)?;

        let uri = element.attribute("uri")?;

        Ok(Self { uri })
    }
}

impl IntoElement for Redirect {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("redirect", Some(XMLNS)).push_attribute("uri", self.uri.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Retract {
    id: String,
}

impl FromElement for Retract {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("retract")?;
        element.check_namespace(XMLNS)?;

        let id = element.attribute("id")?;

        Ok(Self { id })
    }
}

impl IntoElement for Retract {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("retract", Some(XMLNS)).push_attribute("id", self.id.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Subscription {
    expiry: Option<DateTime<Utc>>,
    jid: JID,
    node: Option<String>,
    subid: Option<String>,
    subscription: Option<SubscriptionState>,
}

impl FromElement for Subscription {
    fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("subscription")?;
        element.check_namespace(XMLNS)?;

        let expiry = element.attribute_opt("expiry")?;
        let jid = element.attribute("jid")?;
        let node = element.attribute_opt("node")?;
        let subid = element.attribute_opt("subid")?;
        let subscription = element.attribute_opt("subscription")?;

        Ok(Self {
            expiry,
            jid,
            node,
            subid,
            subscription,
        })
    }
}

impl IntoElement for Subscription {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("subscription", Some(XMLNS))
            .push_attribute_opt("expiry", self.expiry)
            .push_attribute("jid", self.jid.clone())
            .push_attribute_opt("node", self.node.clone())
            .push_attribute_opt("subid", self.subid.clone())
            .push_attribute_opt("subscription", self.subscription.clone())
    }
}

#[derive(Clone, Debug)]
pub enum SubscriptionState {
    None,
    Pending,
    Subscribed,
    Unconfigured,
}

impl FromStr for SubscriptionState {
    type Err = DeserializeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "none" => Ok(Self::None),
            "pending" => Ok(Self::Pending),
            "subscribed" => Ok(Self::Subscribed),
            "unconfigured" => Ok(Self::Unconfigured),
            s => Err(DeserializeError::FromStr(s.to_owned())),
        }
    }
}

impl ToString for SubscriptionState {
    fn to_string(&self) -> String {
        match self {
            SubscriptionState::None => "none",
            SubscriptionState::Pending => "pending",
            SubscriptionState::Subscribed => "subscribed",
            SubscriptionState::Unconfigured => "unconfigured",
        }
        .to_owned()
    }
}