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








                                        

                            

                                  





































































































































































































































































































                                                                                                   



                                 































                                                                                        
                                
                 


                                  






























                                                                                        

                                



                                       
                     


                              



                                                                                               







                                                                                        

                                            




                                                           


                                                  



                                                                    

                                                                                   










































                                                                                        

                         
































































































































































































































































































                                                                                        
use std::str::FromStr;

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

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

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

#[derive(Clone, Debug)]
pub enum Pubsub {
    Create(Create, Option<Configure>),
    Subscribe(Option<Subscribe>, Option<Options>),
    Publish(Publish, Option<PublishOptions>),
    Affiliations(Affiliations),
    Default(Default),
    Items(Items),
    Retract(Retract),
    Subscription(Subscription),
    Subscriptions(Subscriptions),
    Unsubscribe(Unsubscribe),
    Pubsub,
}

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

        let first_child = element.pop_child_opt::<Element>()?;

        if let Some(first_child) = first_child {
            match first_child.identify() {
                (Some(XMLNS), "create") => {
                    let create = Create::from_element(first_child)?;
                    let configure = element.pop_child_opt()?;
                    Ok(Self::Create(create, configure))
                }
                (Some(XMLNS), "subscribe") => {
                    let subscribe = Subscribe::from_element(first_child)?;
                    let options = element.pop_child_opt()?;
                    Ok(Self::Subscribe(Some(subscribe), options))
                }
                (Some(XMLNS), "options") => {
                    let options = Options::from_element(first_child)?;
                    Ok(Self::Subscribe(None, Some(options)))
                }
                (Some(XMLNS), "publish") => {
                    let publish = Publish::from_element(first_child)?;
                    let publish_options = element.pop_child_opt()?;
                    Ok(Self::Publish(publish, publish_options))
                }
                (Some(XMLNS), "affiliations") => {
                    Ok(Self::Affiliations(Affiliations::from_element(first_child)?))
                }
                (Some(XMLNS), "default") => Ok(Self::Default(Default::from_element(first_child)?)),
                (Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(first_child)?)),
                (Some(XMLNS), "retract") => Ok(Self::Retract(Retract::from_element(first_child)?)),
                (Some(XMLNS), "subscription") => {
                    Ok(Self::Subscription(Subscription::from_element(first_child)?))
                }
                (Some(XMLNS), "subscriptions") => Ok(Self::Subscriptions(
                    Subscriptions::from_element(first_child)?,
                )),
                (Some(XMLNS), "unsubscribe") => {
                    Ok(Self::Unsubscribe(Unsubscribe::from_element(first_child)?))
                }
                _ => Err(peanuts::DeserializeError::UnexpectedElement(first_child)),
            }
        } else {
            Ok(Pubsub::Pubsub)
        }
    }
}

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

        match self {
            Pubsub::Create(create, configure) => element
                .push_child(create.clone())
                .push_child_opt(configure.clone()),
            Pubsub::Subscribe(subscribe, options) => element
                .push_child_opt(subscribe.clone())
                .push_child_opt(options.clone()),
            Pubsub::Publish(publish, publish_options) => element
                .push_child(publish.clone())
                .push_child_opt(publish_options.clone()),
            Pubsub::Affiliations(affiliations) => element.push_child(affiliations.clone()),
            Pubsub::Default(default) => element.push_child(default.clone()),
            Pubsub::Items(items) => element.push_child(items.clone()),
            Pubsub::Retract(retract) => element.push_child(retract.clone()),
            Pubsub::Subscription(subscription) => element.push_child(subscription.clone()),
            Pubsub::Subscriptions(subscriptions) => element.push_child(subscriptions.clone()),
            Pubsub::Unsubscribe(unsubscribe) => element.push_child(unsubscribe.clone()),
            Pubsub::Pubsub => element,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Affiliations {
    node: Option<String>,
    affiliations: Vec<Affiliation>,
}

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

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

        let affiliations = element.pop_children()?;

        Ok(Self { node, affiliations })
    }
}

impl IntoElement for Affiliations {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("affilations", Some(XMLNS))
            .push_attribute_opt("node", self.node.clone())
            .push_children(self.affiliations.clone())
    }
}

// empty
#[derive(Clone, Debug)]
pub struct Affiliation {
    affiliation: AffiliationType,
    node: String,
}

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

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

        Ok(Self { affiliation, node })
    }
}

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

#[derive(Clone, Debug)]
pub enum AffiliationType {
    Member,
    None,
    Outcast,
    Owner,
    Publisher,
    PublishOnly,
}

impl FromStr for AffiliationType {
    type Err = DeserializeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "member" => Ok(AffiliationType::Member),
            "none" => Ok(AffiliationType::None),
            "outcast" => Ok(AffiliationType::Outcast),
            "owner" => Ok(AffiliationType::Owner),
            "publisher" => Ok(AffiliationType::Publisher),
            "publish-only" => Ok(AffiliationType::PublishOnly),
            s => Err(DeserializeError::FromStr(s.to_string())),
        }
    }
}

impl ToString for AffiliationType {
    fn to_string(&self) -> String {
        match self {
            AffiliationType::Member => "member",
            AffiliationType::None => "none",
            AffiliationType::Outcast => "outcast",
            AffiliationType::Owner => "owner",
            AffiliationType::Publisher => "publisher",
            AffiliationType::PublishOnly => "publish-only",
        }
        .to_owned()
    }
}

#[derive(Clone, Debug)]
pub struct Configure(Option<X>);

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

        Ok(Configure(element.pop_child_opt()?))
    }
}

impl IntoElement for Configure {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("configure", Some(XMLNS)).push_child_opt(self.0.clone())
    }
}

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

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

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

        Ok(Self { node })
    }
}

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

#[derive(Clone, Debug)]
pub struct Default {
    node: Option<String>,
    // default leaf
    r#type: Option<DefaultType>,
    default: Option<X>,
}

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

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

        let default = element.pop_child_opt()?;

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

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

#[derive(Clone, Debug)]
pub enum DefaultType {
    Collection,
    Leaf,
}

impl FromStr for DefaultType {
    type Err = DeserializeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "collection" => Ok(Self::Collection),
            "leaf" => Ok(Self::Leaf),
            s => Err(DeserializeError::FromStr(s.to_string())),
        }
    }
}

impl ToString for DefaultType {
    fn to_string(&self) -> String {
        match self {
            DefaultType::Collection => "collection",
            DefaultType::Leaf => "leaf",
        }
        .to_owned()
    }
}

#[derive(Clone, Debug)]
pub struct Items {
    pub max_items: Option<usize>,
    pub node: String,
    pub subid: Option<String>,
    pub items: Vec<Item>,
}

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

        let max_items = element.attribute_opt("max_items")?;
        let node = element.attribute("node")?;
        let subid = element.attribute_opt("subid")?;

        let items = element.pop_children()?;

        Ok(Self {
            max_items,
            node,
            subid,
            items,
        })
    }
}

impl IntoElement for Items {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("items", Some(XMLNS))
            .push_attribute_opt("max_items", self.max_items)
            .push_attribute("node", self.node.clone())
            .push_attribute_opt("subid", self.subid.clone())
            .push_children(self.items.clone())
    }
}

#[derive(Clone, Debug, Default)]
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),
    #[cfg(feature = "xep_0084")]
    AvatarData(xep_0084::Data),
    #[cfg(feature = "xep_0084")]
    AvatarMetadata(xep_0084::Metadata),
    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)?)),
            #[cfg(feature = "xep_0084")]
            (Some(xep_0084::data::XMLNS), "data") => {
                Ok(Content::AvatarData(xep_0084::Data::from_element(element)?))
            }
            #[cfg(feature = "xep_0084")]
            (Some(xep_0084::metadata::XMLNS), "metadata") => Ok(Content::AvatarMetadata(
                xep_0084::Metadata::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(),
            #[cfg(feature = "xep_0084")]
            Content::AvatarData(data) => data.builder(),
            #[cfg(feature = "xep_0084")]
            Content::AvatarMetadata(metadata) => metadata.builder(),
            Content::Unknown(_e) => panic!("unknown content cannot be serialized"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Options {
    jid: JID,
    node: Option<String>,
    subid: Option<String>,
    options: Option<X>,
}

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

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

        let options = element.pop_child_opt()?;

        Ok(Self {
            jid,
            node,
            subid,
            options,
        })
    }
}

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

#[derive(Clone, Debug)]
pub struct Publish {
    pub node: String,
    pub items: Vec<Item>,
}

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

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

        let items = element.pop_children()?;

        Ok(Self { node, items })
    }
}

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

#[derive(Clone, Debug)]
pub struct PublishOptions(pub Option<X>);

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

        Ok(Self(element.pop_child_opt()?))
    }
}

impl IntoElement for PublishOptions {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("publish-options", Some(XMLNS)).push_child_opt(self.0.clone())
    }
}

#[derive(Clone, Debug)]
pub struct Retract {
    node: String,
    notify: Option<bool>,
    /// minOccurs = 1
    items: Vec<Item>,
}

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

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

        let items = element.pop_children()?;

        Ok(Self {
            node,
            notify,
            items,
        })
    }
}

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

#[derive(Clone, Debug)]
pub struct SubscribeOptions {
    // <required/> element
    required: bool,
}

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

        let required;
        if let Some(_) = element.pop_child_opt::<Required>()? {
            required = true;
        } else {
            required = false;
        }

        Ok(Self { required })
    }
}

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

        if self.required {
            builder.push_child(Required)
        } else {
            builder
        }
    }
}

pub struct Required;

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

        Ok(Self)
    }
}

impl IntoElement for Required {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("required", Some(XMLNS))
    }
}

#[derive(Clone, Debug)]
pub struct Subscribe {
    jid: JID,
    node: Option<String>,
}

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

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

        Ok(Self { jid, node })
    }
}

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

#[derive(Clone, Debug)]
pub struct Subscriptions {
    node: Option<String>,
    subscriptions: Vec<Subscription>,
}

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

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

        let subscriptions = element.pop_children()?;

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

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

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

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

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

        let subscribe_options = element.pop_child_opt()?;

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

impl IntoElement for Subscription {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("subscription", Some(XMLNS))
            .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())
            .push_child_opt(self.subscribe_options.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_string()
    }
}

#[derive(Clone, Debug)]
pub struct Unsubscribe {
    jid: JID,
    node: Option<String>,
    subid: Option<String>,
}

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

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

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

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

// TODO: enforce 'empty'