2020//
2121// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later
2222
23- use std:: hash:: Hasher ;
24-
2523use crate :: {
2624 contacts:: { Card , CardId } ,
2725 error:: Result ,
@@ -41,7 +39,7 @@ impl JSContactVersion for JSContactVersion1 {}
4139
4240pub struct CardDeserializer ;
4341
44- #[ derive( Debug ) ]
42+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
4543pub struct JSContact < T : JSContactVersion > (
4644 json_types:: JsonCardValue ,
4745 std:: marker:: PhantomData < * const T > ,
@@ -68,25 +66,34 @@ impl<V: JSContactVersion> TryInto<Card> for JSContact<V> {
6866 type Error = crate :: error:: Error ;
6967
7068 fn try_into ( self ) -> Result < Card > {
69+ let json_types:: JsonCardValue {
70+ uid, name, email, ..
71+ } = self . 0 ;
7172 let mut card = Card :: new ( ) ;
72- card. set_id ( CardId :: Hash ( {
73- let mut hasher = std:: collections:: hash_map:: DefaultHasher :: new ( ) ;
74- hasher. write ( self . 0 . uid . as_bytes ( ) ) ;
75- hasher. finish ( )
76- } ) ) ;
77- card. set_name ( self . 0 . name . full . clone ( ) . unwrap_or_default ( ) ) ;
78- if let Some ( e) = self . 0 . email . get_index ( 0 ) {
73+ card. set_id ( CardId :: from ( uid) ) ;
74+ if let Some ( name) = name. full {
75+ card. set_name ( name) ;
76+ }
77+ if let Some ( e) = email. get_index ( 0 ) {
7978 card. set_email ( e. 1 . address . to_string ( ) ) ;
8079 }
8180
8281 Ok ( card)
8382 }
8483}
8584
85+ impl From < json_types:: JsonCardValue > for JSContact < JSContactVersion1 > {
86+ fn from ( val : json_types:: JsonCardValue ) -> Self {
87+ Self ( val, std:: marker:: PhantomData :: < * const JSContactVersion1 > )
88+ }
89+ }
90+
8691pub mod json_types {
8792 use indexmap:: IndexMap ;
8893 use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
8994
95+ use crate :: utils:: datetime:: UnixTimestamp ;
96+
9097 macro_rules! impl_json_type_struct_serde {
9198 ( $t: tt, $s: literal) => {
9299 #[ derive( Clone , Copy , PartialEq , Eq , Debug , Default ) ]
@@ -155,6 +162,44 @@ pub mod json_types {
155162 }
156163 }
157164
165+ #[ derive( Clone , Copy , Debug , Default , Eq , PartialEq ) ]
166+ pub struct UTCDateTime ( pub UnixTimestamp ) ;
167+
168+ impl Serialize for UTCDateTime {
169+ fn serialize < S > ( & self , serializer : S ) -> std:: result:: Result < S :: Ok , S :: Error >
170+ where
171+ S : Serializer ,
172+ {
173+ use crate :: utils:: datetime:: { formats:: RFC3339_DATETIME_Z , timestamp_to_string_utc} ;
174+
175+ serializer. serialize_str ( & timestamp_to_string_utc (
176+ self . 0 ,
177+ Some ( RFC3339_DATETIME_Z ) ,
178+ true ,
179+ ) )
180+ }
181+ }
182+
183+ impl < ' de > Deserialize < ' de > for UTCDateTime {
184+ fn deserialize < D > ( deserializer : D ) -> std:: result:: Result < Self , D :: Error >
185+ where
186+ D : Deserializer < ' de > ,
187+ {
188+ use crate :: utils:: datetime:: {
189+ formats:: RFC3339_DATETIME_Z , parse_timestamp_from_string,
190+ } ;
191+
192+ let s = <& ' de str >:: deserialize ( deserializer) ?;
193+ let Ok ( ( _, val) ) = parse_timestamp_from_string ( s, RFC3339_DATETIME_Z ) else {
194+ return Err ( serde:: de:: Error :: custom ( format ! (
195+ r#"expected UTCDateTime value, found `{}`"# ,
196+ s
197+ ) ) ) ;
198+ } ;
199+ Ok ( Self ( val) )
200+ }
201+ }
202+
158203 #[ derive( Clone , Copy , Debug , Default , Deserialize , Eq , PartialEq , Serialize ) ]
159204 #[ serde( rename_all = "lowercase" ) ]
160205 pub enum JsonCardKind {
@@ -181,7 +226,14 @@ pub mod json_types {
181226 pub version : JsonCardVersion ,
182227 pub uid : String ,
183228 #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
184- pub created : Option < String > ,
229+ pub created : Option < UTCDateTime > ,
230+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
231+ pub updated : Option < UTCDateTime > ,
232+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
233+ /// The language tag, as defined in `RFC5646`, that best describes the
234+ /// language used for text in the card, optionally including
235+ /// additional information such as the script.
236+ pub language : Option < String > ,
185237 #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
186238 pub kind : Option < JsonCardKind > ,
187239 pub name : JsonCardName ,
@@ -220,12 +272,17 @@ pub mod json_types {
220272
221273 #[ test]
222274 fn test_addressbook_jscontact ( ) {
275+ use super :: JSContactVersion1 ;
276+ use crate :: contacts:: { jscontact:: JSContact , Card , CardId } ;
277+
223278 assert_eq ! (
224279 JsonCardValue {
225280 __type: JsonCardType ,
226281 version: JsonCardVersion :: _1_0,
227282 uid: "22B2C7DF-9120-4969-8460-05956FE6B065" . to_string( ) ,
228283 created: None ,
284+ updated: None ,
285+ language: None ,
229286 kind: Some ( JsonCardKind :: Individual ) ,
230287 name: JsonCardName {
231288 __type: None ,
@@ -264,5 +321,52 @@ pub mod json_types {
264321 )
265322 . unwrap( ) ,
266323 ) ;
324+ assert_eq ! (
325+ Card {
326+ last_edited: 1727155810 ,
327+ ..<JSContact <JSContactVersion1 > as std:: convert:: TryInto <Card >>:: try_into(
328+ JSContact :: <JSContactVersion1 >:: from(
329+ serde_json:: from_str:: <JsonCardValue >(
330+ r#"{
331+ "@type": "Card",
332+ "version": "1.0",
333+ "uid": "22B2C7DF-9120-4969-8460-05956FE6B065",
334+ "kind": "individual",
335+ "email": {
336+ "main": {
337+ 338+ }
339+ },
340+ "name": {
341+ "components": [],
342+ "full": "full_name",
343+ "isOrdered": true
344+ }
345+ }"#
346+ )
347+ . unwrap( )
348+ )
349+ )
350+ . unwrap( )
351+ } ,
352+ Card {
353+ id: CardId :: Uuid (
354+ uuid:: Uuid :: try_parse( "22B2C7DF-9120-4969-8460-05956FE6B065" ) . unwrap( )
355+ ) ,
356+ title: "" . into( ) ,
357+ name: "full_name" . into( ) ,
358+ additionalname: "" . into( ) ,
359+ name_prefix: "" . into( ) ,
360+ name_suffix: "" . into( ) ,
361+ birthday: None ,
362+ email
: "[email protected] " . into
( ) , 363+ url: "" . into( ) ,
364+ key: "" . into( ) ,
365+ color: 0 ,
366+ last_edited: 1727155810 ,
367+ extra_properties: indexmap:: indexmap! { } ,
368+ external_resource: false
369+ } ,
370+ ) ;
267371 }
268372}
0 commit comments