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