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