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 extensions : Extensions ,
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 . 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 ) ]
106134pub 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