Skip to main content

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