1use crate::StringEnum;
28use crate::iq::spec::IqSpec;
29use crate::request::InfoQuery;
30use wacore_binary::builder::NodeBuilder;
31use wacore_binary::jid::{Jid, SERVER_JID};
32use wacore_binary::node::{Node, NodeContent};
33
34pub const PRIVACY_NAMESPACE: &str = "privacy";
36
37#[derive(Debug, Clone, PartialEq, Eq, StringEnum)]
39pub enum PrivacyCategory {
40 #[str = "last"]
42 Last,
43 #[str = "online"]
45 Online,
46 #[str = "profile"]
48 Profile,
49 #[str = "status"]
51 Status,
52 #[str = "groupadd"]
54 GroupAdd,
55 #[str = "readreceipts"]
57 ReadReceipts,
58 #[string_fallback]
60 Other(String),
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, StringEnum)]
65pub enum PrivacyValue {
66 #[str = "all"]
68 All,
69 #[str = "contacts"]
71 Contacts,
72 #[str = "none"]
74 None,
75 #[str = "contact_blacklist"]
77 ContactBlacklist,
78 #[str = "match_last_seen"]
80 MatchLastSeen,
81 #[string_fallback]
83 Other(String),
84}
85
86#[derive(Debug, Clone)]
88pub struct PrivacySetting {
89 pub category: PrivacyCategory,
91 pub value: PrivacyValue,
93}
94
95#[derive(Debug, Clone, Default)]
97pub struct PrivacySettingsResponse {
98 pub settings: Vec<PrivacySetting>,
100}
101
102impl PrivacySettingsResponse {
103 pub fn get(&self, category: &PrivacyCategory) -> Option<&PrivacySetting> {
105 self.settings.iter().find(|s| &s.category == category)
106 }
107
108 pub fn get_value(&self, category: &PrivacyCategory) -> Option<&PrivacyValue> {
110 self.get(category).map(|s| &s.value)
111 }
112}
113
114#[derive(Debug, Clone, Default)]
116pub struct PrivacySettingsSpec;
117
118impl PrivacySettingsSpec {
119 pub fn new() -> Self {
121 Self
122 }
123}
124
125impl IqSpec for PrivacySettingsSpec {
126 type Response = PrivacySettingsResponse;
127
128 fn build_iq(&self) -> InfoQuery<'static> {
129 InfoQuery::get(
130 PRIVACY_NAMESPACE,
131 Jid::new("", SERVER_JID),
132 Some(NodeContent::Nodes(vec![
133 NodeBuilder::new("privacy").build(),
134 ])),
135 )
136 }
137
138 fn parse_response(&self, response: &Node) -> Result<Self::Response, anyhow::Error> {
139 use crate::iq::node::{optional_attr, required_child};
140
141 let privacy_node = required_child(response, "privacy")?;
142
143 let mut settings = Vec::new();
144 for child in privacy_node.get_children_by_tag("category") {
145 let name = optional_attr(child, "name")
146 .ok_or_else(|| anyhow::anyhow!("missing name in category"))?;
147 let value = optional_attr(child, "value")
148 .ok_or_else(|| anyhow::anyhow!("missing value in category"))?;
149
150 settings.push(PrivacySetting {
151 category: PrivacyCategory::from(name.as_ref()),
152 value: PrivacyValue::from(value.as_ref()),
153 });
154 }
155
156 Ok(PrivacySettingsResponse { settings })
157 }
158}
159
160#[derive(Debug, Clone)]
170pub struct SetPrivacySettingSpec {
171 pub category: String,
172 pub value: String,
173}
174
175impl SetPrivacySettingSpec {
176 pub fn new(category: impl Into<String>, value: impl Into<String>) -> Self {
177 Self {
178 category: category.into(),
179 value: value.into(),
180 }
181 }
182}
183
184impl IqSpec for SetPrivacySettingSpec {
185 type Response = ();
186
187 fn build_iq(&self) -> InfoQuery<'static> {
188 InfoQuery::set(
189 PRIVACY_NAMESPACE,
190 Jid::new("", SERVER_JID),
191 Some(NodeContent::Nodes(vec![
192 NodeBuilder::new("privacy")
193 .children([NodeBuilder::new("category")
194 .attr("name", &*self.category)
195 .attr("value", &*self.value)
196 .build()])
197 .build(),
198 ])),
199 )
200 }
201
202 fn parse_response(&self, _response: &Node) -> Result<Self::Response, anyhow::Error> {
203 Ok(())
204 }
205}
206
207#[derive(Debug, Clone)]
215pub struct SetDefaultDisappearingModeSpec {
216 pub duration: u32,
217}
218
219impl SetDefaultDisappearingModeSpec {
220 pub fn new(duration: u32) -> Self {
221 Self { duration }
222 }
223}
224
225impl IqSpec for SetDefaultDisappearingModeSpec {
226 type Response = ();
227
228 fn build_iq(&self) -> InfoQuery<'static> {
229 InfoQuery::set(
230 "disappearing_mode",
231 Jid::new("", SERVER_JID),
232 Some(NodeContent::Nodes(vec![
233 NodeBuilder::new("disappearing_mode")
234 .attr("duration", self.duration.to_string())
235 .build(),
236 ])),
237 )
238 }
239
240 fn parse_response(&self, _response: &Node) -> Result<Self::Response, anyhow::Error> {
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_privacy_settings_spec_build_iq() {
251 let spec = PrivacySettingsSpec::new();
252 let iq = spec.build_iq();
253
254 assert_eq!(iq.namespace, PRIVACY_NAMESPACE);
255 assert_eq!(iq.query_type, crate::request::InfoQueryType::Get);
256
257 if let Some(NodeContent::Nodes(nodes)) = &iq.content {
258 assert_eq!(nodes.len(), 1);
259 assert_eq!(nodes[0].tag, "privacy");
260 } else {
261 panic!("Expected NodeContent::Nodes");
262 }
263 }
264
265 #[test]
266 fn test_privacy_settings_spec_parse_response() {
267 let spec = PrivacySettingsSpec::new();
268 let response = NodeBuilder::new("iq")
269 .attr("type", "result")
270 .children([NodeBuilder::new("privacy")
271 .children([
272 NodeBuilder::new("category")
273 .attr("name", "last")
274 .attr("value", "all")
275 .build(),
276 NodeBuilder::new("category")
277 .attr("name", "profile")
278 .attr("value", "contacts")
279 .build(),
280 NodeBuilder::new("category")
281 .attr("name", "status")
282 .attr("value", "none")
283 .build(),
284 ])
285 .build()])
286 .build();
287
288 let result = spec.parse_response(&response).unwrap();
289 assert_eq!(result.settings.len(), 3);
290
291 assert_eq!(result.settings[0].category, PrivacyCategory::Last);
292 assert_eq!(result.settings[0].value, PrivacyValue::All);
293
294 assert_eq!(result.settings[1].category, PrivacyCategory::Profile);
295 assert_eq!(result.settings[1].value, PrivacyValue::Contacts);
296
297 assert_eq!(result.settings[2].category, PrivacyCategory::Status);
298 assert_eq!(result.settings[2].value, PrivacyValue::None);
299 }
300
301 #[test]
302 fn test_privacy_settings_response_get() {
303 let response = PrivacySettingsResponse {
304 settings: vec![
305 PrivacySetting {
306 category: PrivacyCategory::Last,
307 value: PrivacyValue::All,
308 },
309 PrivacySetting {
310 category: PrivacyCategory::Profile,
311 value: PrivacyValue::Contacts,
312 },
313 ],
314 };
315
316 assert_eq!(
317 response.get_value(&PrivacyCategory::Last),
318 Some(&PrivacyValue::All)
319 );
320 assert_eq!(
321 response.get_value(&PrivacyCategory::Profile),
322 Some(&PrivacyValue::Contacts)
323 );
324 assert_eq!(response.get_value(&PrivacyCategory::Online), None);
325 }
326
327 #[test]
328 fn test_privacy_category_from_str() {
329 assert_eq!(PrivacyCategory::from("last"), PrivacyCategory::Last);
330 assert_eq!(PrivacyCategory::from("online"), PrivacyCategory::Online);
331 assert_eq!(
332 PrivacyCategory::from("unknown"),
333 PrivacyCategory::Other("unknown".to_string())
334 );
335 }
336
337 #[test]
338 fn test_privacy_value_from_str() {
339 assert_eq!(PrivacyValue::from("all"), PrivacyValue::All);
340 assert_eq!(PrivacyValue::from("contacts"), PrivacyValue::Contacts);
341 assert_eq!(PrivacyValue::from("none"), PrivacyValue::None);
342 assert_eq!(
343 PrivacyValue::from("unknown"),
344 PrivacyValue::Other("unknown".to_string())
345 );
346 }
347}