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