Skip to content

Commit 3fc16f4

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

File tree

1 file changed

+64
-11
lines changed

1 file changed

+64
-11
lines changed

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

Lines changed: 64 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 extensions: Extensions,
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.extensions.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 Extensions(HashMap<String, String>);
112+
113+
impl Deref for Extensions {
114+
type Target = HashMap<String, String>;
115+
116+
fn deref(&self) -> &Self::Target {
117+
&self.0
118+
}
119+
}
120+
121+
impl DerefMut for Extensions {
122+
fn deref_mut(&mut self) -> &mut Self::Target {
123+
&mut self.0
124+
}
125+
}
126+
127+
impl FromIterator<(String, String)> for Extensions {
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,11 @@ 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
194+
.extensions
195+
.insert(key.to_string(), child.text_value()?);
196+
}
164197
_ => (),
165198
}
166199
}
@@ -184,6 +217,10 @@ impl From<VCard4> for Element {
184217
.append_all_values(vcard.title, "title", "text", |v| v.value)
185218
.append_all_values(vcard.tel, "tel", "text", |v| v.value)
186219
.append_all_values(vcard.url, "url", "uri", |v| v.value)
220+
.append_all(vcard.extensions.iter().map(|(key, value)| {
221+
Element::builder(key, ns::VCARD4)
222+
.append(Element::builder("text", ns::VCARD4).append(value.clone()))
223+
}))
187224
.build()
188225
}
189226
}
@@ -459,13 +496,9 @@ mod tests {
459496
fn test_serialize_vcard() -> Result<()> {
460497
let vcard = VCard4 {
461498
adr: vec![Adr {
462-
code: vec![],
463499
country: vec!["France, French Republic".to_string()],
464-
ext: vec![],
465500
locality: vec!["Nantes".to_string()],
466-
pobox: vec![],
467-
region: vec![],
468-
street: vec![],
501+
..Default::default()
469502
}],
470503
email: vec![Email {
471504
value: "[email protected]".to_string(),
@@ -476,7 +509,7 @@ mod tests {
476509
n: vec![Name {
477510
surname: Some("Saliou".to_string()),
478511
given: Some("Valerian".to_string()),
479-
additional: None,
512+
..Default::default()
480513
}],
481514
impp: vec![Impp {
482515
value: "xmpp:[email protected]".to_string(),
@@ -489,17 +522,37 @@ mod tests {
489522
value: "Another nickname".to_string(),
490523
},
491524
],
492-
note: vec![],
493-
org: vec![],
494-
tel: vec![],
495-
title: vec![],
496-
role: vec![],
497525
url: vec![URL {
498526
value: "https://prose.org/".to_string(),
499527
}],
528+
..Default::default()
500529
};
501530

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

0 commit comments

Comments
 (0)