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









                                           
                       
                  

                            





















                                                                                        
                       
                 
                                                       
                               
                                                     
                  
                                
                 
                                                      
                             
                                          

                                           




















                                                                                        
                                                                  


























                                                           
                                                                  



                                               
                                      



































                                                               
                       
                                         
                                     
















                                                                                                 
use std::str::FromStr;

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

pub const XMLNS: &str = "jabber:iq:roster";

#[derive(Debug, Clone)]
pub struct Query {
    pub ver: Option<String>,
    pub 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, Debug)]
pub struct Item {
    /// signals subscription pre-approval (server only)
    pub approved: Option<bool>,
    /// signals subscription sub-states (server only)
    pub ask: bool,
    /// uniquely identifies item
    pub jid: JID,
    /// handle that is determined by user, not contact
    pub name: Option<String>,
    /// state of the presence subscription
    pub subscription: Option<Subscription>,
    pub 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")?;
        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_opt("subscription", self.subscription)
            .push_children(self.groups.clone())
    }
}

#[derive(Default, Clone, Copy, Debug)]
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, Debug)]
// TODO: check if should be option or not
pub struct Group(pub 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())
    }
}