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+
69use anyhow:: Result ;
710use minidom:: { Element , ElementBuilder } ;
811use 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
2933impl 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 ) ]
106134pub 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