1#[cfg(any(feature = "starttls-rust", feature = "starttls-native"))]
8use crate::tokio_xmpp::connect::{DnsConfig, StartTlsServerConnector};
9use alloc::sync::Arc;
10use core::str::FromStr;
11use std::collections::HashMap;
12use tokio::sync::RwLock;
13
14use crate::{
15 jid::{BareJid, Jid, ResourceRef},
16 parsers::{
17 disco::{DiscoInfoResult, Feature, Identity},
18 ns,
19 },
20 tokio_xmpp::{connect::ServerConnector, xmlstream::Timeouts, Client as TokioXmppClient},
21 Agent, ClientFeature, RoomNick,
22};
23
24#[derive(Debug)]
25pub enum ClientType {
26 Bot,
27 Pc,
28}
29
30impl Default for ClientType {
31 fn default() -> Self {
32 ClientType::Bot
33 }
34}
35
36impl ToString for ClientType {
37 fn to_string(&self) -> String {
38 String::from(match self {
39 ClientType::Bot => "bot",
40 ClientType::Pc => "pc",
41 })
42 }
43}
44
45pub struct ClientBuilder<'a, C: ServerConnector> {
46 jid: BareJid,
47 password: &'a str,
48 server_connector: C,
49 website: String,
50 default_nick: RoomNick,
51 lang: Vec<String>,
52 disco: (ClientType, String),
53 features: Vec<ClientFeature>,
54 resource: Option<String>,
55 timeouts: Timeouts,
56}
57
58#[cfg(any(feature = "starttls-rust", feature = "starttls-native"))]
59impl ClientBuilder<'_, StartTlsServerConnector> {
60 pub fn new<'a>(jid: BareJid, password: &'a str) -> ClientBuilder<'a, StartTlsServerConnector> {
61 Self::new_with_connector(
62 jid.clone(),
63 password,
64 StartTlsServerConnector(DnsConfig::srv_default_client(jid.domain())),
65 )
66 }
67}
68
69impl<C: ServerConnector> ClientBuilder<'_, C> {
70 pub fn new_with_connector<'a>(
71 jid: BareJid,
72 password: &'a str,
73 server_connector: C,
74 ) -> ClientBuilder<'a, C> {
75 ClientBuilder {
76 jid,
77 password,
78 server_connector,
79 website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"),
80 default_nick: RoomNick::from_str("xmpp-rs").unwrap(),
81 lang: vec![String::from("en")],
82 disco: (ClientType::default(), String::from("tokio-xmpp")),
83 features: vec![],
84 resource: None,
85 timeouts: Timeouts::default(),
86 }
87 }
88
89 pub fn set_resource(mut self, resource: &str) -> Self {
91 self.resource = Some(resource.to_string());
92 self
93 }
94
95 pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
96 self.disco = (type_, String::from(name));
97 self
98 }
99
100 pub fn set_website(mut self, url: &str) -> Self {
101 self.website = String::from(url);
102 self
103 }
104
105 pub fn set_default_nick(mut self, nick: impl AsRef<ResourceRef>) -> Self {
106 self.default_nick = RoomNick::from_resource_ref(nick.as_ref());
107 self
108 }
109
110 pub fn set_lang(mut self, lang: Vec<String>) -> Self {
111 self.lang = lang;
112 self
113 }
114
115 pub fn set_timeouts(mut self, timeouts: Timeouts) -> Self {
120 self.timeouts = timeouts;
121 self
122 }
123
124 pub fn enable_feature(mut self, feature: ClientFeature) -> Self {
125 self.features.push(feature);
126 self
127 }
128
129 fn make_disco(&self) -> DiscoInfoResult {
130 let identities = vec![Identity::new(
131 "client",
132 self.disco.0.to_string(),
133 "en",
134 self.disco.1.to_string(),
135 )];
136 let mut features = vec![Feature::new(ns::DISCO_INFO)];
137 #[cfg(feature = "avatars")]
138 {
139 if self.features.contains(&ClientFeature::Avatars) {
140 features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA)));
141 }
142 }
143 if self.features.contains(&ClientFeature::JoinRooms) {
144 features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2)));
145 }
146 DiscoInfoResult {
147 node: None,
148 identities,
149 features,
150 extensions: vec![],
151 }
152 }
153
154 pub fn build(self) -> Agent {
155 let jid: Jid = if let Some(resource) = &self.resource {
156 self.jid.with_resource_str(resource).unwrap().into()
157 } else {
158 self.jid.clone().into()
159 };
160
161 let client = TokioXmppClient::new_with_connector(
162 jid,
163 self.password,
164 self.server_connector.clone(),
165 self.timeouts,
166 );
167 self.build_impl(client)
168 }
169
170 pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent {
172 let disco = self.make_disco();
173 let node = self.website;
174
175 Agent {
176 client,
177 default_nick: Arc::new(RwLock::new(self.default_nick)),
178 lang: Arc::new(self.lang),
179 disco,
180 node,
181 uploads: Vec::new(),
182 awaiting_disco_bookmarks_type: false,
183 rooms_joined: HashMap::new(),
184 rooms_joining: HashMap::new(),
185 rooms_leaving: HashMap::new(),
186 }
187 }
188}