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::connect::ServerConnector;
  9use tokio_xmpp::{
 10    jid::{BareJid, Jid},
 11    parsers::{
 12        disco::{DiscoInfoResult, Feature, Identity},
 13        ns,
 14    },
 15    AsyncClient as TokioXmppClient, AsyncConfig,
 16};
 17
 18use crate::{Agent, ClientFeature};
 19
 20#[derive(Debug)]
 21pub enum ClientType {
 22    Bot,
 23    Pc,
 24}
 25
 26impl Default for ClientType {
 27    fn default() -> Self {
 28        ClientType::Bot
 29    }
 30}
 31
 32impl ToString for ClientType {
 33    fn to_string(&self) -> String {
 34        String::from(match self {
 35            ClientType::Bot => "bot",
 36            ClientType::Pc => "pc",
 37        })
 38    }
 39}
 40
 41pub struct ClientBuilder<'a, C: ServerConnector> {
 42    jid: BareJid,
 43    password: &'a str,
 44    server_connector: C,
 45    website: String,
 46    default_nick: String,
 47    lang: Vec<String>,
 48    disco: (ClientType, String),
 49    features: Vec<ClientFeature>,
 50    resource: Option<String>,
 51}
 52
 53#[cfg(any(feature = "starttls-rust", feature = "starttls-native"))]
 54impl ClientBuilder<'_, tokio_xmpp::starttls::ServerConfig> {
 55    pub fn new<'a>(
 56        jid: BareJid,
 57        password: &'a str,
 58    ) -> ClientBuilder<'a, tokio_xmpp::starttls::ServerConfig> {
 59        Self::new_with_connector(jid, password, tokio_xmpp::starttls::ServerConfig::UseSrv)
 60    }
 61}
 62
 63impl<C: ServerConnector> ClientBuilder<'_, C> {
 64    pub fn new_with_connector<'a>(
 65        jid: BareJid,
 66        password: &'a str,
 67        server_connector: C,
 68    ) -> ClientBuilder<'a, C> {
 69        ClientBuilder {
 70            jid,
 71            password,
 72            server_connector,
 73            website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"),
 74            default_nick: String::from("xmpp-rs"),
 75            lang: vec![String::from("en")],
 76            disco: (ClientType::default(), String::from("tokio-xmpp")),
 77            features: vec![],
 78            resource: None,
 79        }
 80    }
 81
 82    /// Optionally set a resource associated to this device on the client
 83    pub fn set_resource(mut self, resource: &str) -> Self {
 84        self.resource = Some(resource.to_string());
 85        self
 86    }
 87
 88    pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
 89        self.disco = (type_, String::from(name));
 90        self
 91    }
 92
 93    pub fn set_website(mut self, url: &str) -> Self {
 94        self.website = String::from(url);
 95        self
 96    }
 97
 98    pub fn set_default_nick(mut self, nick: &str) -> Self {
 99        self.default_nick = String::from(nick);
100        self
101    }
102
103    pub fn set_lang(mut self, lang: Vec<String>) -> Self {
104        self.lang = lang;
105        self
106    }
107
108    pub fn enable_feature(mut self, feature: ClientFeature) -> Self {
109        self.features.push(feature);
110        self
111    }
112
113    fn make_disco(&self) -> DiscoInfoResult {
114        let identities = vec![Identity::new(
115            "client",
116            self.disco.0.to_string(),
117            "en",
118            self.disco.1.to_string(),
119        )];
120        let mut features = vec![Feature::new(ns::DISCO_INFO)];
121        #[cfg(feature = "avatars")]
122        {
123            if self.features.contains(&ClientFeature::Avatars) {
124                features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
125            }
126        }
127        if self.features.contains(&ClientFeature::JoinRooms) {
128            features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
129        }
130        DiscoInfoResult {
131            node: None,
132            identities,
133            features,
134            extensions: vec![],
135        }
136    }
137
138    pub fn build(self) -> Agent<C> {
139        let jid: Jid = if let Some(resource) = &self.resource {
140            self.jid.with_resource_str(resource).unwrap().into()
141        } else {
142            self.jid.clone().into()
143        };
144
145        let config = AsyncConfig {
146            jid,
147            password: self.password.into(),
148            server: self.server_connector.clone(),
149        };
150        let client = TokioXmppClient::new_with_config(config);
151        self.build_impl(client)
152    }
153
154    // This function is meant to be used for testing build
155    pub(crate) fn build_impl(self, client: TokioXmppClient<C>) -> Agent<C> {
156        let disco = self.make_disco();
157        let node = self.website;
158
159        Agent {
160            client,
161            default_nick: Arc::new(RwLock::new(self.default_nick)),
162            lang: Arc::new(self.lang),
163            disco,
164            node,
165            uploads: Vec::new(),
166            awaiting_disco_bookmarks_type: false,
167        }
168    }
169}