1#[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 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 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 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}