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