proto-types 0.1.0

Rust types generated from the google.protobuf and buf.validate protobuf packages, plus extra helpers for implementing validation with the protocheck crate.
Documentation
use std::fmt;

use chrono::{DateTime, Utc};
use serde::{de, ser};

use crate::Timestamp;

impl ser::Serialize for Timestamp {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: ser::Serializer,
  {
    let mut ts_normalized = *self;
    ts_normalized.normalize();

    let datetime_opt =
      DateTime::<Utc>::from_timestamp(ts_normalized.seconds, ts_normalized.nanos as u32);

    if let Some(datetime) = datetime_opt {
      let rfc3339_string = datetime.to_rfc3339();
      serializer.serialize_str(&rfc3339_string)
    } else {
      Err(ser::Error::custom(format!(
        "Invalid timestamp during serialization: seconds={}, nanos={}",
        ts_normalized.seconds, ts_normalized.nanos
      )))
    }
  }
}

impl<'de> de::Deserialize<'de> for Timestamp {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: de::Deserializer<'de>,
  {
    struct TimestampVisitor;

    impl de::Visitor<'_> for TimestampVisitor {
      type Value = Timestamp;

      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an RFC 3339 formatted timestamp string")
      }

      fn visit_str<E>(self, value: &str) -> Result<Timestamp, E>
      where
        E: de::Error,
      {
        let datetime = value
          .parse::<DateTime<Utc>>()
          .map_err(|e| de::Error::custom(format!("Invalid timestamp string format: {}", e)))?;

        let prost_timestamp = Timestamp {
          seconds: datetime.timestamp(),
          nanos: datetime.timestamp_subsec_nanos() as i32,
        };

        Ok(prost_timestamp)
      }
    }

    deserializer.deserialize_str(TimestampVisitor)
  }
}