use chrono::{DateTime, FixedOffset}; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer}; use std::fmt; use std::marker::PhantomData; pub const ISSUE_QUERY: &str = r#"query ($repo: ID!, $id: String) { project (fullPath: $repo) { issue (iid: $id) { id descriptionHtml author { username } title createdAt webUrl notes { nodes { id author { username } body bodyHtml createdAt url } } } } }"#; pub const MR_QUERY: &str = r#"query ($repo: ID!, $id: String!) { project (fullPath: $repo) { mergeRequest (iid: $id) { id descriptionHtml author { username } title createdAt webUrl notes { nodes { id author { username } body bodyHtml createdAt url } } } } }"#; #[derive(Deserialize, Debug)] pub struct Data { pub data: Project, } #[derive(Deserialize, Debug)] pub struct Project { pub project: IssueOrMR, } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub enum IssueOrMR { Issue(IssueInfo), MergeRequest(IssueInfo), } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct IssueInfo { pub id: String, pub description_html: String, pub author: Author, pub title: String, #[serde(deserialize_with = "deserialize_datetime")] pub created_at: DateTime, pub web_url: String, pub notes: NoteConnection, } #[derive(Deserialize, Debug)] pub struct Author { pub username: String, } #[derive(Deserialize, Debug)] pub struct NoteConnection { pub nodes: Vec, } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Note { pub id: String, pub author: Author, pub body: String, pub body_html: String, #[serde(deserialize_with = "deserialize_datetime")] pub created_at: DateTime, pub url: 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)) }