use serde::{
de::{Error, Unexpected},
Deserialize, Serialize,
};
use uuid::Uuid;
use crate::models::{
color::Color,
flair::{FlairTextColor, FlairType},
fullname::FullName,
richtext::Richtext,
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Author {
#[serde(flatten)]
pub author_info: AuthorInfo,
#[serde(rename = "author_is_blocked")]
pub is_blocked: bool,
#[serde(flatten)]
pub flair: AuthorFlair,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub enum AuthorInfo {
Deleted,
Author {
#[serde(rename = "author")]
name: String,
#[serde(rename = "author_fullname")]
fullname: FullName,
#[serde(rename = "author_premium")]
premium: bool,
#[serde(rename = "author_patreon_flair")]
patreon_flair: bool,
},
}
impl<'de> Deserialize<'de> for AuthorInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
struct RawAuthorInfo {
#[serde(rename = "author")]
name: String,
#[serde(rename = "author_fullname")]
fullname: Option<FullName>,
#[serde(rename = "author_premium")]
premium: Option<bool>,
#[serde(rename = "author_patreon_flair")]
patreon_flair: Option<bool>,
}
let info: RawAuthorInfo =
serde_json::from_value(serde_json::Value::deserialize(deserializer)?)
.map_err(D::Error::custom)?;
match info {
RawAuthorInfo {
name,
fullname: None,
premium: None,
patreon_flair: None,
} if name == "[deleted]" => Ok(AuthorInfo::Deleted),
RawAuthorInfo { name, .. } if name == "[deleted]" => Err(D::Error::invalid_value(
Unexpected::Other(
"[deleted] requires that all other author info fields are either null or not present",
),
&"[deleted] requires that all other author info fields are either null or not present",
)),
RawAuthorInfo {
name,
fullname: Some(fullname),
premium: Some(premium),
patreon_flair: Some(patreon_flair),
} => Ok(AuthorInfo::Author {
name,
fullname,
patreon_flair,
premium,
}),
_ => Err(D::Error::invalid_value(
Unexpected::Other(
"author with name requires that all other author info fields are present",
),
&"author with name requires that all other author info fields are present",
)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AuthorFlair {
#[serde(deserialize_with = "Color::from_hex_str")]
#[serde(rename = "author_flair_background_color")]
pub background_color: Option<Color>,
#[serde(rename = "author_flair_template_id")]
pub template_id: Option<AuthorFlairTemplateId>,
#[serde(rename = "author_flair_css_class")]
pub css_class: Option<String>,
#[serde(rename = "author_flair_richtext")]
pub richtext: Option<Vec<Richtext>>,
#[serde(with = "serde_with::rust::string_empty_as_none")]
#[serde(rename = "author_flair_text_color")]
pub text_color: Option<FlairTextColor>,
#[serde(rename = "author_flair_type")]
pub ty: Option<FlairType>,
#[serde(rename = "author_flair_text")]
pub text: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub struct AuthorFlairTemplateId(Uuid);
#[cfg(test)]
mod test_author {
#![allow(clippy::non_ascii_literal)]
use super::*;
#[test]
fn test_deserialize() {
let json = r##"
{
"author": "dermeister1985",
"author_flair_background_color": "#dadada",
"author_flair_css_class": "flazy",
"author_flair_richtext": [
{
"e": "text",
"t": "лл"
}
],
"author_flair_template_id": "3e1b76b8-6266-11e9-ad04-0e5925592ac0",
"author_flair_text": "лл",
"author_flair_text_color": "dark",
"author_flair_type": "richtext",
"author_fullname": "t2_3mdzj3s7",
"author_is_blocked": false,
"author_patreon_flair": false,
"author_premium": false
}"##;
dbg!(serde_json::from_str::<Author>(json).unwrap());
}
}