Skip to content

Commit 5f7e831

Browse files
committed
feat: Add support for private properties in vCard4
1 parent f9d0624 commit 5f7e831

File tree

1 file changed

+62
-11
lines changed

1 file changed

+62
-11
lines changed

crates/prose-xmpp/src/stanza/vcard4.rs

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// Copyright: 2023, Marc Bauer <[email protected]>
44
// License: Mozilla Public License v2.0 (MPL v2.0)
55

6+
use std::collections::HashMap;
7+
use std::ops::{Deref, DerefMut};
8+
69
use anyhow::Result;
710
use minidom::{Element, ElementBuilder};
811
use xmpp_parsers::iq::IqSetPayload;
@@ -24,6 +27,7 @@ pub struct VCard4 {
2427
pub tel: Vec<Tel>,
2528
pub title: Vec<Title>,
2629
pub url: Vec<URL>,
30+
pub private_: PrivateProperties,
2731
}
2832

2933
impl VCard4 {
@@ -44,6 +48,7 @@ impl VCard4 {
4448
&& self.tel.is_empty()
4549
&& self.title.is_empty()
4650
&& self.url.is_empty()
51+
&& self.private_.is_empty()
4752
}
4853
}
4954

@@ -102,6 +107,29 @@ pub struct Org {
102107
pub value: String,
103108
}
104109

110+
#[derive(Debug, Clone, PartialEq, Default)]
111+
pub struct PrivateProperties(HashMap<String, String>);
112+
113+
impl Deref for PrivateProperties {
114+
type Target = HashMap<String, String>;
115+
116+
fn deref(&self) -> &Self::Target {
117+
&self.0
118+
}
119+
}
120+
121+
impl DerefMut for PrivateProperties {
122+
fn deref_mut(&mut self) -> &mut Self::Target {
123+
&mut self.0
124+
}
125+
}
126+
127+
impl FromIterator<(String, String)> for PrivateProperties {
128+
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
129+
Self(HashMap::from_iter(iter))
130+
}
131+
}
132+
105133
#[derive(Debug, Clone, PartialEq)]
106134
pub struct Role {
107135
pub value: String,
@@ -161,6 +189,9 @@ impl TryFrom<Element> for VCard4 {
161189
"role" => vcard.role.push(Role {
162190
value: child.text_value()?,
163191
}),
192+
key if key.starts_with("x-") => {
193+
vcard.private_.insert(key.to_string(), child.text_value()?);
194+
}
164195
_ => (),
165196
}
166197
}
@@ -184,6 +215,10 @@ impl From<VCard4> for Element {
184215
.append_all_values(vcard.title, "title", "text", |v| v.value)
185216
.append_all_values(vcard.tel, "tel", "text", |v| v.value)
186217
.append_all_values(vcard.url, "url", "uri", |v| v.value)
218+
.append_all(vcard.private_.iter().map(|(key, value)| {
219+
Element::builder(key, ns::VCARD4)
220+
.append(Element::builder("text", ns::VCARD4).append(value.clone()))
221+
}))
187222
.build()
188223
}
189224
}
@@ -459,13 +494,9 @@ mod tests {
459494
fn test_serialize_vcard() -> Result<()> {
460495
let vcard = VCard4 {
461496
adr: vec![Adr {
462-
code: vec![],
463497
country: vec!["France, French Republic".to_string()],
464-
ext: vec![],
465498
locality: vec!["Nantes".to_string()],
466-
pobox: vec![],
467-
region: vec![],
468-
street: vec![],
499+
..Default::default()
469500
}],
470501
email: vec![Email {
471502
value: "[email protected]".to_string(),
@@ -476,7 +507,7 @@ mod tests {
476507
n: vec![Name {
477508
surname: Some("Saliou".to_string()),
478509
given: Some("Valerian".to_string()),
479-
additional: None,
510+
..Default::default()
480511
}],
481512
impp: vec![Impp {
482513
value: "xmpp:[email protected]".to_string(),
@@ -489,17 +520,37 @@ mod tests {
489520
value: "Another nickname".to_string(),
490521
},
491522
],
492-
note: vec![],
493-
org: vec![],
494-
tel: vec![],
495-
title: vec![],
496-
role: vec![],
497523
url: vec![URL {
498524
value: "https://prose.org/".to_string(),
499525
}],
526+
..Default::default()
500527
};
501528

502529
assert_eq!(VCard4::try_from(Element::from(vcard.clone()))?, vcard);
503530
Ok(())
504531
}
532+
533+
/// We store workspace details in a vCard4, and need to store non-standard properties.
534+
/// This test ensures private properties are parsed correctly.
535+
#[test]
536+
fn test_private_properties() -> Result<()> {
537+
let vcard = VCard4 {
538+
fn_: vec![Fn_ {
539+
value: "Prose".to_string(),
540+
}],
541+
private_: vec![("x-accent-color", "#2d8deb")]
542+
.into_iter()
543+
.map(|(k, v)| (k.to_string(), v.to_string()))
544+
.collect(),
545+
..Default::default()
546+
};
547+
548+
assert_eq!(VCard4::try_from(Element::from(vcard.clone()))?, vcard);
549+
// Check that values can be read
550+
assert_eq!(
551+
vcard.private_.get(&"x-accent-color".to_string()),
552+
Some(&"#2d8deb".to_string())
553+
);
554+
Ok(())
555+
}
505556
}

0 commit comments

Comments
 (0)