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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use std::collections::HashMap;
use url::Url;

/// A u8 was not a valid media type
#[derive(Debug)]
pub struct InvalidMediaTypeError(u8);

impl std::fmt::Display for InvalidMediaTypeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "`{}` is not a valid media type", self.0)
    }
}

impl std::error::Error for InvalidMediaTypeError {}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, serde::Deserialize)]
#[serde(try_from = "u8")]
pub enum MediaType {
    /// A Photo
    Photo,

    /// A video
    Video,

    /// A carousel
    Carousel,
}

impl TryFrom<u8> for MediaType {
    type Error = InvalidMediaTypeError;

    fn try_from(n: u8) -> Result<Self, Self::Error> {
        match n {
            1 => Ok(Self::Photo),
            2 => Ok(Self::Video),
            8 => Ok(Self::Carousel),
            _ => Err(InvalidMediaTypeError(n)),
        }
    }
}

/// AdditionalDataLoaded
#[derive(Debug, serde::Deserialize)]
pub struct AdditionalDataLoaded {
    /// ?
    pub num_results: u32,

    /// ?
    pub items: Vec<AdditionalDataLoadedItem>,

    /// ?
    pub auto_load_more_enabled: bool,

    /// ?
    pub more_available: bool,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

#[derive(Debug, serde::Deserialize)]
pub struct AdditionalDataLoadedItem {
    /// The media type
    pub media_type: MediaType,

    /// Versions of a video post.
    ///
    /// Only present on video posts
    pub video_versions: Option<Vec<VideoVersion>>,

    /// Versions of an image post
    pub image_versions2: Option<ImageVersions2>,

    /// Carousel media
    pub carousel_media: Option<Vec<CarouselMediaItem>>,

    /// The post code
    pub code: String,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

impl AdditionalDataLoadedItem {
    /// Get the best image_versions2 candidate
    pub fn get_best_image_versions2_candidate(&self) -> Option<&ImageVersions2Candidate> {
        self.image_versions2.as_ref()?.get_best()
    }

    /// Get the best video version
    pub fn get_best_video_version(&self) -> Option<&VideoVersion> {
        self.video_versions
            .as_ref()?
            .iter()
            .max_by_key(|video_version| video_version.height)
    }
}

#[derive(Debug, serde::Deserialize)]
pub struct VideoVersion {
    /// The height in pixels
    pub height: u32,

    /// The width in pixels
    pub width: u32,

    /// ?
    #[serde(rename = "type")]
    pub kind: u32,

    /// the src url
    pub url: Url,

    /// ?
    pub id: String,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

/// The image_versions2 field
#[derive(Debug, serde::Deserialize)]
pub struct ImageVersions2 {
    /// Candidate images
    pub candidates: Vec<ImageVersions2Candidate>,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

impl ImageVersions2 {
    /// Get the best candidate
    pub fn get_best(&self) -> Option<&ImageVersions2Candidate> {
        self.candidates
            .iter()
            .max_by_key(|image_versions2_candidate| image_versions2_candidate.height)
    }
}

/// A ImageVersions2 candidate
#[derive(Debug, serde::Deserialize)]
pub struct ImageVersions2Candidate {
    /// The image height in pixels
    pub width: u32,

    /// The image width in pixels
    pub height: u32,

    /// The url
    pub url: Url,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

/// An item in carousel_media
#[derive(Debug, serde::Deserialize)]
pub struct CarouselMediaItem {
    /// The media type
    pub media_type: MediaType,

    /// Image versions
    pub image_versions2: Option<ImageVersions2>,

    /// Extra fields
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

impl CarouselMediaItem {
    /// Get the best image_versions2 candidate
    pub fn get_best_image_versions2_candidate(&self) -> Option<&ImageVersions2Candidate> {
        self.image_versions2.as_ref()?.get_best()
    }
}