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};
  9
 10use crate::{
 11    Agent, ClientFeature, ClientType, Config, RoomNick,
 12    jid::{BareJid, Jid, ResourceRef},
 13    parsers::{
 14        disco::{DiscoInfoResult, Feature, Identity},
 15        ns,
 16    },
 17    tokio_xmpp::{Client as TokioXmppClient, connect::ServerConnector, xmlstream::Timeouts},
 18};
 19
 20pub struct ClientBuilder<'a, C: ServerConnector> {
 21    jid: BareJid,
 22    password: &'a str,
 23    server_connector: C,
 24    config: Config,
 25    features: Vec<ClientFeature>,
 26    resource: Option<String>,
 27    timeouts: Timeouts,
 28}
 29
 30#[cfg(feature = "starttls")]
 31impl ClientBuilder<'_, StartTlsServerConnector> {
 32    pub fn new<'a>(jid: BareJid, password: &'a str) -> ClientBuilder<'a, StartTlsServerConnector> {
 33        Self::new_with_connector(
 34            jid.clone(),
 35            password,
 36            StartTlsServerConnector(DnsConfig::srv_default_client(jid.domain())),
 37        )
 38    }
 39}
 40
 41impl<C: ServerConnector> ClientBuilder<'_, C> {
 42    pub fn new_with_connector<'a>(
 43        jid: BareJid,
 44        password: &'a str,
 45        server_connector: C,
 46    ) -> ClientBuilder<'a, C> {
 47        ClientBuilder {
 48            jid,
 49            password,
 50            server_connector,
 51            config: Config::default(),
 52            features: vec![],
 53            resource: None,
 54            timeouts: Timeouts::default(),
 55        }
 56    }
 57
 58    /// Optionally set a resource associated to this device on the client
 59    pub fn set_resource(mut self, resource: &str) -> Self {
 60        self.resource = Some(resource.to_string());
 61        self
 62    }
 63
 64    pub fn set_config(mut self, config: Config) -> Self {
 65        self.config = config;
 66        self
 67    }
 68
 69    pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
 70        self.config.disco = (type_, String::from(name));
 71        self
 72    }
 73
 74    pub fn set_website(mut self, url: &str) -> Self {
 75        self.config.website = String::from(url);
 76        self
 77    }
 78
 79    pub fn set_default_nick(mut self, nick: impl AsRef<ResourceRef>) -> Self {
 80        self.config.default_nick = RoomNick::from_resource_ref(nick.as_ref());
 81        self
 82    }
 83
 84    pub fn set_lang(mut self, lang: Vec<String>) -> Self {
 85        self.config.lang = lang;
 86        self
 87    }
 88
 89    /// Configure the timeouts used.
 90    ///
 91    /// See [`Timeouts`] for more information on the semantics and the
 92    /// defaults (which are used unless you call this method).
 93    pub fn set_timeouts(mut self, timeouts: Timeouts) -> Self {
 94        self.timeouts = timeouts;
 95        self
 96    }
 97
 98    pub fn enable_feature(mut self, feature: ClientFeature) -> Self {
 99        self.features.push(feature);
100        self
101    }
102
103    fn make_disco(&self) -> DiscoInfoResult {
104        let identities = vec![Identity::new(
105            "client",
106            self.config.disco.0.to_string(),
107            "en",
108            self.config.disco.1.to_string(),
109        )];
110        let mut features = vec![Feature::new(ns::DISCO_INFO)];
111        #[cfg(feature = "avatars")]
112        {
113            if self.features.contains(&ClientFeature::Avatars) {
114                features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
115            }
116        }
117        if self.features.contains(&ClientFeature::JoinRooms) {
118            features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
119        }
120        DiscoInfoResult {
121            node: None,
122            identities,
123            features,
124            extensions: vec![],
125        }
126    }
127
128    pub fn build(self) -> Agent {
129        let jid: Jid = if let Some(resource) = &self.resource {
130            self.jid.with_resource_str(resource).unwrap().into()
131        } else {
132            self.jid.clone().into()
133        };
134
135        let client = TokioXmppClient::new_with_connector(
136            jid,
137            self.password,
138            self.server_connector.clone(),
139            self.timeouts,
140        );
141        self.build_impl(client)
142    }
143
144    // This function is meant to be used for testing build
145    pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent {
146        let disco = self.make_disco();
147
148        Agent::new(client, self.config, disco)
149    }
150}