reddit_rs/models/
author.rs

1use serde::{
2    de::{Error, Unexpected},
3    Deserialize, Serialize,
4};
5use uuid::Uuid;
6
7use crate::models::{
8    color::Color,
9    flair::{FlairTextColor, FlairType},
10    fullname::FullName,
11    richtext::Richtext,
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct Author {
16    #[serde(flatten)]
17    pub author_info: AuthorInfo,
18    #[serde(rename = "author_is_blocked")]
19    pub is_blocked: bool,
20    #[serde(flatten)]
21    pub flair: AuthorFlair,
22}
23
24#[derive(Debug, Clone, Serialize, PartialEq)]
25pub enum AuthorInfo {
26    Deleted,
27    Author {
28        #[serde(rename = "author")]
29        name: String,
30        #[serde(rename = "author_fullname")]
31        fullname: FullName,
32        #[serde(rename = "author_premium")]
33        premium: bool,
34        #[serde(rename = "author_patreon_flair")]
35        patreon_flair: bool,
36    },
37}
38
39impl<'de> Deserialize<'de> for AuthorInfo {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: serde::Deserializer<'de>,
43    {
44        #[derive(Debug, Deserialize)]
45        struct RawAuthorInfo {
46            #[serde(rename = "author")]
47            name: String,
48            #[serde(rename = "author_fullname")]
49            fullname: Option<FullName>,
50            #[serde(rename = "author_premium")]
51            premium: Option<bool>,
52            #[serde(rename = "author_patreon_flair")]
53            patreon_flair: Option<bool>,
54        }
55
56        let info: RawAuthorInfo =
57            serde_json::from_value(serde_json::Value::deserialize(deserializer)?)
58                .map_err(D::Error::custom)?;
59
60        match info {
61            RawAuthorInfo {
62                name,
63                fullname: None,
64                premium: None,
65                patreon_flair: None,
66            } if name == "[deleted]" => Ok(AuthorInfo::Deleted),
67            RawAuthorInfo { name, .. } if name == "[deleted]" => Err(D::Error::invalid_value(
68                Unexpected::Other(
69                    "[deleted] requires that all other author info fields are either null or not present",
70                ),
71                &"[deleted] requires that all other author info fields are either null or not present",
72            )),
73            RawAuthorInfo {
74                name,
75                fullname: Some(fullname),
76                premium: Some(premium),
77                patreon_flair: Some(patreon_flair),
78            } => Ok(AuthorInfo::Author {
79                name,
80                fullname,
81                patreon_flair,
82                premium,
83            }),
84            _ => Err(D::Error::invalid_value(
85                Unexpected::Other(
86                    "author with name requires that all other author info fields are present",
87                ),
88                &"author with name requires that all other author info fields are present",
89            )),
90        }
91    }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub struct AuthorFlair {
96    #[serde(deserialize_with = "Color::from_hex_str")]
97    #[serde(rename = "author_flair_background_color")]
98    pub background_color: Option<Color>,
99
100    #[serde(rename = "author_flair_template_id")]
101    pub template_id: Option<AuthorFlairTemplateId>,
102
103    #[serde(rename = "author_flair_css_class")]
104    pub css_class: Option<String>,
105
106    #[serde(rename = "author_flair_richtext")]
107    pub richtext: Option<Vec<Richtext>>,
108
109    #[serde(with = "serde_with::rust::string_empty_as_none")]
110    #[serde(rename = "author_flair_text_color")]
111    pub text_color: Option<FlairTextColor>,
112
113    #[serde(rename = "author_flair_type")]
114    pub ty: Option<FlairType>,
115
116    #[serde(rename = "author_flair_text")]
117    pub text: Option<String>,
118}
119
120#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
121pub struct AuthorFlairTemplateId(Uuid);
122
123#[cfg(test)]
124mod test_author {
125    #![allow(clippy::non_ascii_literal)] // testing non-ascii deserialization
126
127    use super::*;
128
129    #[test]
130    fn test_deserialize() {
131        let json = r##"
132        {
133            "author": "dermeister1985",
134            "author_flair_background_color": "#dadada",
135            "author_flair_css_class": "flazy",
136            "author_flair_richtext": [
137              {
138                "e": "text",
139                "t": "лл"
140              }
141            ],
142            "author_flair_template_id": "3e1b76b8-6266-11e9-ad04-0e5925592ac0",
143            "author_flair_text": "лл",
144            "author_flair_text_color": "dark",
145            "author_flair_type": "richtext",
146            "author_fullname": "t2_3mdzj3s7",
147            "author_is_blocked": false,
148            "author_patreon_flair": false,
149            "author_premium": false
150        }"##;
151
152        dbg!(serde_json::from_str::<Author>(json).unwrap());
153    }
154}