1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
use derive_getters::Getters;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use super::listing::KindContainer;

/// Empty string means no replies, otherwise it's a JSON array of RedditComment objects.
fn deserialize_replies<'de, D>(deserializer: D) -> Result<Option<KindContainer>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let replies = Value::deserialize(deserializer)?;

    // When there are no replies, Reddit returns an empty string, not a JSON null,
    // but we want to check just in case. Else, it's a KindContainer with RedditComment objects,
    // but that's not important at this moment, we just treat it a value.
    match &replies {
        Value::Null | Value::String(_) => Ok(None),
        _ => Ok(serde_json::from_value(replies).map_err(serde::de::Error::custom)?),
    }
}

/// Represents a reply to a [Post](super::post::Post)
#[derive(Getters, Debug, Clone, Deserialize, Serialize)]
pub struct Comment {
    /// ID of the subreddit, eg. t5_2qh3s
    subreddit_id: String,
    /// Name of the subreddit, eg. Polska
    subreddit: String,
    /// Replies to the comment, if any
    #[serde(deserialize_with = "deserialize_replies")]
    replies: Option<KindContainer>,
    /// Comment author, eg. spez, without `u/`
    author: String,
    /// Comment text
    body: String,
    /// Standard url to the comment, without `.json` at the end
    permalink: String,
    /// UNIX timestamp of the comment creation
    created_utc: f32,
    /// Depth of the comment in the thread. 0 is the top-level comment, 1 is a reply to the top-level comment, etc.
    ///
    /// When fetching posts/comments from a user, this field is always `None`.
    depth: Option<u32>,
    /// Upvotes - downvotes
    score: i64,
}