Skip to main content

mal_api/anime/
responses.rs

1use std::fmt::Display;
2
3use crate::common::{
4    AlternativeTitles, Genre, MainPicture, Paging, PagingIter, Ranking, RelationType, NSFW,
5};
6use serde::{Deserialize, Serialize};
7use serde_json::{self, Value};
8
9#[derive(Debug, Deserialize, Serialize)]
10pub struct AnimeList {
11    pub data: Vec<AnimeListNode>,
12    pub paging: Paging,
13}
14
15impl PagingIter for AnimeList {
16    type Item = Self;
17
18    fn next_page(&self) -> Option<&String> {
19        self.paging.next.as_ref()
20    }
21
22    fn prev_page(&self) -> Option<&String> {
23        self.paging.previous.as_ref()
24    }
25}
26
27impl Display for AnimeList {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
30    }
31}
32
33#[derive(Debug, Deserialize, Serialize)]
34pub struct AnimeListNode {
35    pub node: AnimeFields,
36
37    /// This field is only present when querying for a User's anime list
38    pub list_status: Option<AnimeListStatus>,
39}
40
41impl Display for AnimeListNode {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
44    }
45}
46
47#[derive(Debug, Deserialize, Serialize)]
48pub struct AnimePicture {
49    pub medium: String,
50    pub large: String,
51}
52
53impl Display for AnimePicture {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
56    }
57}
58
59#[derive(Debug, Deserialize, Serialize, PartialEq)]
60#[serde(rename_all = "snake_case")]
61pub enum AnimeMediaType {
62    Unknown,
63    TvSpecial, // undocumented media type...
64    Tv,
65    Cm, // undocumented media type...
66    Ova,
67    Movie,
68    Special,
69    Ona,
70    Music,
71    Pv, // undocumented media type...
72}
73
74#[derive(Debug, Deserialize, Serialize, PartialEq)]
75#[serde(rename_all = "snake_case")]
76pub enum AnimeStatus {
77    FinishedAiring,
78    CurrentlyAiring,
79    NotYetAired,
80}
81
82#[derive(Debug, Deserialize, Serialize)]
83pub struct AnimeListStatus {
84    pub status: Option<super::requests::UserAnimeListStatus>,
85    pub score: u8,
86    pub num_episodes_watched: u32,
87    pub is_rewatching: bool,
88    pub start_date: Option<String>,
89    pub finish_date: Option<String>,
90    pub priority: u8,
91    pub num_times_rewatched: u32,
92    pub rewatch_value: u8,
93    pub tags: Vec<String>,
94    pub comments: String,
95    pub updated_at: String,
96}
97
98impl Display for AnimeListStatus {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
101    }
102}
103
104#[derive(Debug, Deserialize, Serialize)]
105pub struct StartSeason {
106    pub year: u32,
107    pub season: super::requests::Season,
108}
109
110impl Display for StartSeason {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
113    }
114}
115
116#[derive(Debug, Deserialize, Serialize)]
117pub struct Broadcast {
118    pub day_of_the_week: String,
119    pub start_time: Option<String>,
120}
121
122impl Display for Broadcast {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
125    }
126}
127
128#[derive(Debug, Deserialize, Serialize, PartialEq)]
129#[serde(rename_all = "snake_case")]
130pub enum Source {
131    Other,
132    Original,
133    Manga,
134    #[serde(rename = "4_koma_manga")]
135    KomaManga,
136    WebManga,
137    DigitalMedia,
138    Novel,
139    LightNovel,
140    VisualNovel,
141    Game,
142    CardGame,
143    Book,
144    PictureBook,
145    MixedMedia, // undocumented source...
146    Radio,
147    Music,
148    WebNovel, // undocumented source...
149}
150
151#[derive(Debug, Deserialize, Serialize, PartialEq)]
152#[serde(rename_all = "lowercase")]
153pub enum Rating {
154    G,
155    PG,
156    #[serde(rename = "pg_13")]
157    PG13,
158    R,
159    #[serde(rename = "r+")]
160    RP,
161    RX,
162}
163
164#[derive(Debug, Deserialize, Serialize)]
165pub struct Studio {
166    pub id: u32,
167    pub name: String,
168}
169
170impl Display for Studio {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
173    }
174}
175
176// Wrap everything in Options since user controls what fields should be returned
177#[derive(Debug, Deserialize, Serialize)]
178pub struct AnimeFields {
179    pub id: u32,
180    pub title: String,
181    pub main_picture: Option<MainPicture>,
182    pub alternative_titles: Option<AlternativeTitles>,
183    pub start_date: Option<String>,
184    pub end_date: Option<String>,
185    pub synopsis: Option<String>,
186    pub mean: Option<f32>,
187    pub rank: Option<u32>,
188    pub popularity: Option<u32>,
189    pub num_list_users: Option<u32>,
190    pub num_scoring_users: Option<u32>,
191    pub nsfw: Option<NSFW>,
192    pub genres: Option<Vec<Genre>>,
193    pub created_at: Option<String>,
194    pub updated_at: Option<String>,
195    pub media_type: Option<AnimeMediaType>,
196    pub status: Option<AnimeStatus>,
197    pub my_list_status: Option<AnimeListStatus>,
198    pub num_episodes: Option<u32>,
199    pub start_season: Option<StartSeason>,
200    pub broadcast: Option<Broadcast>,
201    pub source: Option<Source>,
202    pub average_episode_duration: Option<u32>,
203    pub rating: Option<Rating>,
204    pub studios: Option<Vec<Studio>>,
205}
206
207impl Display for AnimeFields {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
210    }
211}
212
213#[derive(Debug, Deserialize, Serialize)]
214pub struct RelatedAnime {
215    pub node: AnimeFields,
216    pub relation_type: RelationType,
217    pub relation_type_formatted: String,
218}
219
220impl Display for RelatedAnime {
221    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
223    }
224}
225
226#[derive(Debug, Deserialize, Serialize)]
227pub struct Recommendations {
228    pub node: AnimeFields,
229    pub num_recommendations: u32,
230}
231
232impl Display for Recommendations {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
235    }
236}
237
238#[derive(Debug, Deserialize, Serialize)]
239pub struct Statistics {
240    pub num_list_users: u32,
241    pub status: StatisticsStatus,
242}
243
244impl Display for Statistics {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
247    }
248}
249
250#[derive(Debug, Deserialize, Serialize)]
251pub struct StatisticsStatus {
252    // MAL returns these as strings, even though docs say they are supposed to be integers
253    // Use custom serializer for these fields to turn the strings into u32
254    #[serde(deserialize_with = "deserialize_string_to_u32")]
255    pub watching: u32,
256    #[serde(deserialize_with = "deserialize_string_to_u32")]
257    pub completed: u32,
258    #[serde(deserialize_with = "deserialize_string_to_u32")]
259    pub on_hold: u32,
260    #[serde(deserialize_with = "deserialize_string_to_u32")]
261    pub dropped: u32,
262    #[serde(deserialize_with = "deserialize_string_to_u32")]
263    pub plan_to_watch: u32,
264}
265
266impl Display for StatisticsStatus {
267    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
269    }
270}
271
272fn deserialize_string_to_u32<'de, D>(deserializer: D) -> Result<u32, D::Error>
273where
274    D: serde::Deserializer<'de>,
275{
276    let value: Value = Deserialize::deserialize(deserializer)?;
277    if let Some(number) = value.as_str().and_then(|s| s.parse().ok()) {
278        Ok(number)
279    } else {
280        Err(serde::de::Error::custom("Invalid value for u32"))
281    }
282}
283
284#[derive(Debug, Deserialize, Serialize)]
285pub struct AnimeDetails {
286    #[serde(flatten)]
287    pub shared_fields: AnimeFields,
288
289    pub pictures: Option<Vec<AnimePicture>>,
290    pub background: Option<String>,
291    pub related_anime: Option<Vec<RelatedAnime>>,
292    pub related_manga: Option<Vec<crate::manga::responses::RelatedManga>>,
293    pub recommendations: Option<Vec<Recommendations>>,
294    pub statistics: Option<Statistics>,
295}
296
297impl Display for AnimeDetails {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
300    }
301}
302
303#[derive(Debug, Deserialize, Serialize)]
304pub struct AnimeRanking {
305    pub data: Vec<AnimeRankingNode>,
306    pub paging: Paging,
307}
308
309impl Display for AnimeRanking {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
312    }
313}
314
315impl PagingIter for AnimeRanking {
316    type Item = Self;
317
318    fn next_page(&self) -> Option<&String> {
319        self.paging.next.as_ref()
320    }
321
322    fn prev_page(&self) -> Option<&String> {
323        self.paging.previous.as_ref()
324    }
325}
326
327#[derive(Debug, Deserialize, Serialize)]
328pub struct AnimeRankingNode {
329    pub node: AnimeFields,
330    pub ranking: Ranking,
331}
332
333impl Display for AnimeRankingNode {
334    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
336    }
337}
338
339#[derive(Debug, Deserialize, Serialize)]
340pub struct SeasonalAnime {
341    pub data: Vec<SeasonalAnimeNode>,
342    pub paging: Paging,
343}
344
345impl Display for SeasonalAnime {
346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
348    }
349}
350
351impl PagingIter for SeasonalAnime {
352    type Item = Self;
353
354    fn next_page(&self) -> Option<&String> {
355        self.paging.next.as_ref()
356    }
357
358    fn prev_page(&self) -> Option<&String> {
359        self.paging.previous.as_ref()
360    }
361}
362
363#[derive(Debug, Deserialize, Serialize)]
364pub struct SeasonalAnimeNode {
365    pub node: AnimeFields,
366}
367
368impl Display for SeasonalAnimeNode {
369    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
371    }
372}
373
374#[derive(Debug, Deserialize, Serialize)]
375pub struct SuggestedAnime {
376    pub data: Vec<SuggestedAnimeNode>,
377    pub paging: Paging,
378}
379
380impl Display for SuggestedAnime {
381    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
382        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
383    }
384}
385
386impl PagingIter for SuggestedAnime {
387    type Item = Self;
388
389    fn next_page(&self) -> Option<&String> {
390        self.paging.next.as_ref()
391    }
392
393    fn prev_page(&self) -> Option<&String> {
394        self.paging.previous.as_ref()
395    }
396}
397
398#[derive(Debug, Deserialize, Serialize)]
399pub struct SuggestedAnimeNode {
400    pub node: AnimeFields,
401}
402
403impl Display for SuggestedAnimeNode {
404    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405        write!(f, "{}", serde_json::to_string(&self).unwrap_or_default())
406    }
407}