use chrono::{DateTime, FixedOffset}; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer}; use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; #[derive(Deserialize, Debug)] pub struct Issue { pub comments_url: String, pub events_url: String, pub html_url: String, pub node_id: String, pub title: String, pub user: User, pub state: String, pub body: String, #[serde(deserialize_with = "deserialize_datetime")] pub created_at: DateTime, } #[derive(Deserialize, Debug)] #[serde(untagged)] pub enum EventType { Comment(Comment), Event(Event), } #[derive(Deserialize, Debug)] pub struct Comment { pub html_url: String, pub node_id: String, pub user: User, #[serde(deserialize_with = "deserialize_datetime")] pub created_at: DateTime, pub body: String, } #[derive(Deserialize, Debug)] pub struct Event { pub url: String, pub id: usize, pub node_id: String, pub actor: User, #[serde(deserialize_with = "deserialize_datetime")] pub created_at: DateTime, pub event: String, #[serde(flatten)] pub extra: HashMap, } #[derive(Deserialize, Debug)] pub struct User { pub login: String, } fn deserialize_datetime<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct DeserializeDateTime(PhantomData T>); impl<'de> Visitor<'de> for DeserializeDateTime> { type Value = DateTime; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("RFC3339 datetime string") } fn visit_str(self, value: &str) -> Result where E: de::Error, { // https://brokenco.de/2020/08/03/serde-deserialize-with-string.html DateTime::parse_from_rfc3339(value).map_err(serde::de::Error::custom) } } deserializer.deserialize_any(DeserializeDateTime(PhantomData)) }