firestore-serde-timestamp 0.1.2

A timestamp serialization helper for firestore-serde.
Documentation
pub mod timestamp {
    use chrono::{DateTime, TimeZone, Timelike, Utc};
    use prost::Message;
    use prost_types::Timestamp;
    use serde::{Deserialize, Deserializer, Serializer};
    use serde_bytes::ByteBuf;

    pub const DATE_MAGIC: &str = "$TimestampValue";

    pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        #[allow(clippy::cast_possible_wrap)]
        let c = Timestamp {
            seconds: date.timestamp(),
            nanos: date.nanosecond() as i32,
        };

        let v = ByteBuf::from(c.encode_to_vec());

        serializer.serialize_newtype_struct(DATE_MAGIC, &v)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let buf = ByteBuf::deserialize(deserializer)?;
        let timestamp = Timestamp::decode(buf.as_slice())
            .expect("Should always be able to decode timestamp because we just encoded it.");
        #[allow(clippy::cast_sign_loss)]
        let datetime = Utc.timestamp(timestamp.seconds, timestamp.nanos as u32);

        Ok(datetime)
    }
}

#[cfg(test)]
mod test {
    use chrono::{DateTime, Utc, TimeZone};
    use firestore_serde::{from_grpc_value, to_grpc_value, ValueDeserializer, ValueSerializer};
    use googapis::google::firestore::v1::{value::ValueType, MapValue, Value};
    use prost_types::Timestamp;
    use serde::{Deserialize, Serialize};

    use crate::timestamp::{deserialize, serialize};

    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct StructWithDate {
        #[serde(with = "crate::timestamp")]
        date: DateTime<Utc>,
    }

    #[test]
    fn test_serialize_date() {
        let date = Utc.timestamp(150, 200);

        let result = serialize(&date, ValueSerializer).unwrap();

        assert_eq!(
            Value {
                value_type: Some(ValueType::TimestampValue(Timestamp {
                    seconds: 150,
                    nanos: 200
                }))
            },
            result
        );

        assert_eq!(date, deserialize(&mut ValueDeserializer(&result)).unwrap());
    }

    #[test]
    fn test_serialize_in_struct() {
        let st = StructWithDate {
            date: Utc.timestamp(150, 200),
        };

        let result = to_grpc_value(&st).unwrap();

        assert_eq!(
            Value {
                value_type: Some(ValueType::MapValue(MapValue {
                    fields: vec![(
                        "date".to_string(),
                        Value {
                            value_type: Some(ValueType::TimestampValue(Timestamp {
                                seconds: 150,
                                nanos: 200
                            }))
                        }
                    )]
                    .into_iter()
                    .collect()
                }))
            },
            result
        );

        assert_eq!(st, from_grpc_value(&result).unwrap());
    }
}