builder.rs

  1// Copyright (c) 2023 xmpp-rs contributors.
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use std::sync::{Arc, RwLock};
  8use tokio_xmpp::{
  9    parsers::{
 10        disco::{DiscoInfoResult, Feature, Identity},
 11        ns,
 12    },
 13    AsyncClient as TokioXmppClient, BareJid, Jid,
 14};
 15
 16use crate::{Agent, ClientFeature};
 17
 18#[derive(Debug)]
 19pub enum ClientType {
 20    Bot,
 21    Pc,
 22}
 23
 24impl Default for ClientType {
 25    fn default() -> Self {
 26        ClientType::Bot
 27    }
 28}
 29
 30impl ToString for ClientType {
 31    fn to_string(&self) -> String {
 32        String::from(match self {
 33            ClientType::Bot => "bot",
 34            ClientType::Pc => "pc",
 35        })
 36    }
 37}
 38
 39pub struct ClientBuilder<'a> {
 40    jid: BareJid,
 41    password: &'a str,
 42    website: String,
 43    default_nick: String,
 44    lang: Vec<String>,
 45    disco: (ClientType, String),
 46    features: Vec<ClientFeature>,
 47    resource: Option<String>,
 48}
 49
 50impl ClientBuilder<'_> {
 51    pub fn new<'a>(jid: BareJid, password: &'a str) -> ClientBuilder<'a> {
 52        ClientBuilder {
 53            jid,
 54            password,
 55            website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"),
 56            default_nick: String::from("xmpp-rs"),
 57            lang: vec![String::from("en")],
 58            disco: (ClientType::default(), String::from("tokio-xmpp")),
 59            features: vec![],
 60            resource: None,
 61        }
 62    }
 63
 64    /// Optionally set a resource associated to this device on the client
 65    pub fn set_resource(mut self, resource: &str) -> Self {
 66        self.resource = Some(resource.to_string());
 67        self
 68    }
 69
 70    pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
 71        self.disco = (type_, String::from(name));
 72        self
 73    }
 74
 75    pub fn set_website(mut self, url: &str) -> Self {
 76        self.website = String::from(url);
 77        self
 78    }
 79
 80    pub fn set_default_nick(mut self, nick: &str) -> Self {
 81        self.default_nick = String::from(nick);
 82        self
 83    }
 84
 85    pub fn set_lang(mut self, lang: Vec<String>) -> Self {
 86        self.lang = lang;
 87        self
 88    }
 89
 90    pub fn enable_feature(mut self, feature: ClientFeature) -> Self {
 91        self.features.push(feature);
 92        self
 93    }
 94
 95    fn make_disco(&self) -> DiscoInfoResult {
 96        let identities = vec![Identity::new(
 97            "client",
 98            self.disco.0.to_string(),
 99            "en",
100            self.disco.1.to_string(),
101        )];
102        let mut features = vec![Feature::new(ns::DISCO_INFO)];
103        #[cfg(feature = "avatars")]
104        {
105            if self.features.contains(&ClientFeature::Avatars) {
106                features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
107            }
108        }
109        if self.features.contains(&ClientFeature::JoinRooms) {
110            features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
111        }
112        DiscoInfoResult {
113            node: None,
114            identities,
115            features,
116            extensions: vec![],
117        }
118    }
119
120    pub fn build(self) -> Agent {
121        let jid: Jid = if let Some(resource) = &self.resource {
122            self.jid.with_resource_str(resource).unwrap().into()
123        } else {
124            self.jid.clone().into()
125        };
126
127        let client = TokioXmppClient::new(jid, self.password);
128        self.build_impl(client)
129    }
130
131    // This function is meant to be used for testing build
132    pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent {
133        let disco = self.make_disco();
134        let node = self.website;
135
136        Agent {
137            client,
138            default_nick: Arc::new(RwLock::new(self.default_nick)),
139            lang: Arc::new(self.lang),
140            disco,
141            node,
142            uploads: Vec::new(),
143            awaiting_disco_bookmarks_type: false,
144        }
145    }
146}