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
  7#[cfg(feature = "starttls")]
  8use crate::tokio_xmpp::connect::{DnsConfig, StartTlsServerConnector};
  9use core::str::FromStr;
 10
 11use crate::{
 12    Agent, ClientFeature, Config, RoomNick,
 13    jid::{BareJid, Jid, ResourceRef},
 14    parsers::{
 15        disco::{DiscoInfoResult, Feature, Identity},
 16        ns,
 17    },
 18    tokio_xmpp::{Client as TokioXmppClient, connect::ServerConnector, xmlstream::Timeouts},
 19};
 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    config: Config,
 47    website: String,
 48    default_nick: RoomNick,
 49    lang: Vec<String>,
 50    disco: (ClientType, String),
 51    features: Vec<ClientFeature>,
 52    resource: Option<String>,
 53    timeouts: Timeouts,
 54}
 55
 56#[cfg(feature = "starttls")]
 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            config: Config::default(),
 78            website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"),
 79            default_nick: RoomNick::from_str("xmpp-rs").unwrap(),
 80            lang: vec![String::from("en")],
 81            disco: (ClientType::default(), String::from("tokio-xmpp")),
 82            features: vec![],
 83            resource: None,
 84            timeouts: Timeouts::default(),
 85        }
 86    }
 87
 88    /// Optionally set a resource associated to this device on the client
 89    pub fn set_resource(mut self, resource: &str) -> Self {
 90        self.resource = Some(resource.to_string());
 91        self
 92    }
 93
 94    pub fn set_config(mut self, config: Config) -> Self {
 95        self.config = config;
 96        self
 97    }
 98
 99    pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
100        self.disco = (type_, String::from(name));
101        self
102    }
103
104    pub fn set_website(mut self, url: &str) -> Self {
105        self.website = String::from(url);
106        self
107    }
108
109    pub fn set_default_nick(mut self, nick: impl AsRef<ResourceRef>) -> Self {
110        self.default_nick = RoomNick::from_resource_ref(nick.as_ref());
111        self
112    }
113
114    pub fn set_lang(mut self, lang: Vec<String>) -> Self {
115        self.lang = lang;
116        self
117    }
118
119    /// Configure the timeouts used.
120    ///
121    /// See [`Timeouts`] for more information on the semantics and the
122    /// defaults (which are used unless you call this method).
123    pub fn set_timeouts(mut self, timeouts: Timeouts) -> Self {
124        self.timeouts = timeouts;
125        self
126    }
127
128    pub fn enable_feature(mut self, feature: ClientFeature) -> Self {
129        self.features.push(feature);
130        self
131    }
132
133    fn make_disco(&self) -> DiscoInfoResult {
134        let identities = vec![Identity::new(
135            "client",
136            self.disco.0.to_string(),
137            "en",
138            self.disco.1.to_string(),
139        )];
140        let mut features = vec![Feature::new(ns::DISCO_INFO)];
141        #[cfg(feature = "avatars")]
142        {
143            if self.features.contains(&ClientFeature::Avatars) {
144                features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
145            }
146        }
147        if self.features.contains(&ClientFeature::JoinRooms) {
148            features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
149        }
150        DiscoInfoResult {
151            node: None,
152            identities,
153            features,
154            extensions: vec![],
155        }
156    }
157
158    pub fn build(self) -> Agent {
159        let jid: Jid = if let Some(resource) = &self.resource {
160            self.jid.with_resource_str(resource).unwrap().into()
161        } else {
162            self.jid.clone().into()
163        };
164
165        let client = TokioXmppClient::new_with_connector(
166            jid,
167            self.password,
168            self.server_connector.clone(),
169            self.timeouts,
170        );
171        self.build_impl(client)
172    }
173
174    // This function is meant to be used for testing build
175    pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent {
176        let disco = self.make_disco();
177        let node = self.website;
178
179        Agent::new(
180            client,
181            self.config,
182            self.default_nick,
183            self.lang,
184            disco,
185            node,
186        )
187    }
188}