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