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