firestore_serde_timestamp/
lib.rs

1pub mod timestamp {
2    use chrono::{DateTime, TimeZone, Timelike, Utc};
3    use prost::Message;
4    use prost_types::Timestamp;
5    use serde::{Deserialize, Deserializer, Serializer};
6    use serde_bytes::ByteBuf;
7
8    pub const DATE_MAGIC: &str = "$TimestampValue";
9
10    pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
11    where
12        S: Serializer,
13    {
14        #[allow(clippy::cast_possible_wrap)]
15        let c = Timestamp {
16            seconds: date.timestamp(),
17            nanos: date.nanosecond() as i32,
18        };
19
20        let v = ByteBuf::from(c.encode_to_vec());
21
22        serializer.serialize_newtype_struct(DATE_MAGIC, &v)
23    }
24
25    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
26    where
27        D: Deserializer<'de>,
28    {
29        let buf = ByteBuf::deserialize(deserializer)?;
30        let timestamp = Timestamp::decode(buf.as_slice())
31            .expect("Should always be able to decode timestamp because we just encoded it.");
32        #[allow(clippy::cast_sign_loss)]
33        let datetime = Utc.timestamp(timestamp.seconds, timestamp.nanos as u32);
34
35        Ok(datetime)
36    }
37}
38
39#[cfg(test)]
40mod test {
41    use chrono::{DateTime, Utc, TimeZone};
42    use firestore_serde::{from_grpc_value, to_grpc_value, ValueDeserializer, ValueSerializer};
43    use googapis::google::firestore::v1::{value::ValueType, MapValue, Value};
44    use prost_types::Timestamp;
45    use serde::{Deserialize, Serialize};
46
47    use crate::timestamp::{deserialize, serialize};
48
49    #[derive(Serialize, Deserialize, PartialEq, Debug)]
50    struct StructWithDate {
51        #[serde(with = "crate::timestamp")]
52        date: DateTime<Utc>,
53    }
54
55    #[test]
56    fn test_serialize_date() {
57        let date = Utc.timestamp(150, 200);
58
59        let result = serialize(&date, ValueSerializer).unwrap();
60
61        assert_eq!(
62            Value {
63                value_type: Some(ValueType::TimestampValue(Timestamp {
64                    seconds: 150,
65                    nanos: 200
66                }))
67            },
68            result
69        );
70
71        assert_eq!(date, deserialize(&mut ValueDeserializer(&result)).unwrap());
72    }
73
74    #[test]
75    fn test_serialize_in_struct() {
76        let st = StructWithDate {
77            date: Utc.timestamp(150, 200),
78        };
79
80        let result = to_grpc_value(&st).unwrap();
81
82        assert_eq!(
83            Value {
84                value_type: Some(ValueType::MapValue(MapValue {
85                    fields: vec![(
86                        "date".to_string(),
87                        Value {
88                            value_type: Some(ValueType::TimestampValue(Timestamp {
89                                seconds: 150,
90                                nanos: 200
91                            }))
92                        }
93                    )]
94                    .into_iter()
95                    .collect()
96                }))
97            },
98            result
99        );
100
101        assert_eq!(st, from_grpc_value(&result).unwrap());
102    }
103}