mal/api/model/
mod.rs

1/// Anime related structs
2pub mod anime;
3pub use anime::*;
4/// Manga related structs
5pub mod manga;
6pub use manga::*;
7/// User related structs
8pub mod user;
9use serde::de::{self, Visitor};
10pub use user::*;
11
12use core::fmt;
13use serde::{Deserialize, Deserializer, Serialize, Serializer};
14use std::fmt::Debug;
15use std::str::FromStr;
16use strum_macros::{Display, EnumString, IntoStaticStr};
17use time::{
18    format_description::{
19        self,
20        well_known::{iso8601, Iso8601},
21    },
22    // format_description::well_known::{iso8601, Iso8601},
23    Date,
24    Month,
25    PrimitiveDateTime,
26    Time,
27};
28
29pub type Page<T> = PageableData<Vec<Node<T>>>;
30pub type Ranking<T> = PageableData<Vec<T>>;
31
32#[derive(Debug, Clone)]
33pub enum RankingType {
34    AnimeRankingType(AnimeRankingType),
35    MangaRankingType(MangaRankingType),
36}
37
38// uniform time format: "2021-08-01T00:00:00.0Z"
39const CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT
40    .set_year_is_six_digits(false)
41    .encode();
42const FORMAT: Iso8601<CONFIG> = Iso8601::<CONFIG>;
43
44#[derive(Clone, Debug, Deserialize, Serialize)]
45pub struct Paging {
46    pub previous: Option<String>,
47    pub next: Option<String>,
48}
49
50#[derive(Clone, Debug, Deserialize, Serialize)]
51pub struct PageableData<D: Clone + Debug> {
52    pub data: D,
53    pub paging: Paging,
54}
55
56#[derive(Clone, Debug, Deserialize, Serialize)]
57pub struct Node<N: Clone + std::fmt::Debug> {
58    pub node: N,
59}
60
61pub enum Media<'a> {
62    Anime(&'a Anime),
63    Manga(&'a Manga),
64}
65
66#[derive(Clone, Debug, Deserialize, Serialize)]
67pub struct Picture {
68    pub large: Option<String>,
69    pub medium: Option<String>,
70}
71
72#[derive(Clone, Debug, Deserialize, Serialize)]
73pub struct AlternativeTitles {
74    pub synonyms: Option<Vec<String>>,
75    pub en: Option<String>,
76    pub jp: Option<String>,
77}
78
79#[derive(Clone, Debug, Deserialize, Serialize)]
80pub struct Genre {
81    pub id: u64,
82    pub name: String,
83}
84
85#[derive(Clone, Debug, PartialEq, EnumString, IntoStaticStr, Display)]
86#[strum(serialize_all = "snake_case")]
87pub enum Season {
88    Winter,
89    Spring,
90    Summer,
91    Fall,
92    Other(String),
93}
94
95#[derive(Clone, Debug)]
96pub struct TimeWrapper {
97    pub time: Time,
98}
99#[derive(Clone, Debug)]
100pub struct DateWrapper {
101    pub date: Date,
102}
103
104#[derive(Clone, Debug)]
105pub struct DateTimeWrapper {
106    pub datetime: PrimitiveDateTime,
107}
108
109#[derive(Clone, Debug, Deserialize, Serialize)]
110pub struct Broadcast {
111    pub day_of_the_week: String,
112    pub start_time: Option<TimeWrapper>,
113}
114
115#[derive(Clone, Debug, Deserialize, Serialize)]
116pub struct Studio {
117    pub id: u64,
118    pub name: String,
119}
120#[derive(Clone, Debug, Deserialize, Serialize)]
121pub struct MediaDetailStatistics {
122    pub num_list_users: u64,
123    pub status: MediaDetailStatisticsStatus,
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct MediaDetailStatisticsStatus {
128    //# TODO: check if this is correct
129    #[serde(deserialize_with = "string_or_int")]
130    pub watching: String,
131    #[serde(deserialize_with = "string_or_int")]
132    pub completed: String,
133    #[serde(deserialize_with = "string_or_int")]
134    pub on_hold: String,
135    #[serde(deserialize_with = "string_or_int")]
136    pub dropped: String,
137    #[serde(deserialize_with = "string_or_int")]
138    pub plan_to_watch: String,
139}
140// this function is used to deserialize the statistics status fields which might be received as either a string or an integer
141fn string_or_int<'de, D>(deserializer: D) -> Result<String, D::Error>
142where
143    D: Deserializer<'de>,
144{
145    struct StringOrIntVisitor;
146
147    impl Visitor<'_> for StringOrIntVisitor {
148        type Value = String;
149
150        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
151            formatter.write_str("an integer or a string")
152        }
153
154        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
155        where
156            E: de::Error,
157        {
158            Ok(value.to_string())
159        }
160
161        fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
162        where
163            E: de::Error,
164        {
165            Ok(value.to_string())
166        }
167
168        fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
169        where
170            E: de::Error,
171        {
172            Ok(value.to_string())
173        }
174    }
175
176    deserializer.deserialize_any(StringOrIntVisitor)
177}
178
179pub const ALL_ANIME_AND_MANGA_FIELDS: &str = "id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,create_at,updated_at,media_type,status,my_list_status,num_episodes,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics,num_volumes,num_chapters,authors,start_season";
180pub const ALL_USER_FIELDS: &str =
181    "id,name,picture,gender,birthday,location,joined_at,anime_statistics,time_zone,is_supporter";
182
183/// Utility to convert a list of fields to a string (in the format expected by query objects)
184pub fn fields_to_string(fields: &[AnimeField]) -> String {
185    fields
186        .iter()
187        .map(|field| field.into())
188        .collect::<Vec<&str>>()
189        .join(",")
190}
191
192#[derive(Clone, Debug, PartialEq, EnumString, IntoStaticStr)]
193#[strum(serialize_all = "snake_case")]
194pub enum NSFW {
195    White,
196    Gray,
197    Black,
198    Other(String),
199}
200
201#[derive(Clone, Debug, Serialize, Deserialize)]
202pub struct RankingInfo {
203    pub rank: u64,
204    pub previous_rank: Option<u64>,
205}
206
207#[derive(Clone, Debug, Deserialize, Serialize)]
208pub struct Person {
209    pub id: u64,
210    pub first_name: Option<String>,
211    pub last_name: Option<String>,
212}
213
214#[derive(Clone, Debug, Deserialize, Serialize)]
215pub struct PersonRole {
216    pub node: Person,
217    pub role: String,
218}
219
220macro_rules! impl_serialize_deserialize {
221    (for $( $t:ty ),+) => {
222        $(
223        impl Serialize for $t {
224            fn serialize<S>(
225                &self,
226                serializer: S,
227            ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
228            where
229                S: Serializer,
230            {
231                serializer.serialize_str(self.into())
232            }
233        }
234
235        impl<'de> Deserialize<'de> for $t {
236            fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
237            where
238                D: Deserializer<'de>,
239            {
240                let s = String::deserialize(deserializer)?;
241                match Self::from_str(s.as_str()) {
242                    Ok(n) => Ok(n),
243                    Err(_) => Ok(Self::Other(s)),
244                }
245            }
246
247            fn deserialize_in_place<D>(
248                deserializer: D,
249                place: &mut Self,
250            ) -> Result<(), <D as Deserializer<'de>>::Error>
251            where
252                D: Deserializer<'de>,
253            {
254                let s = String::deserialize(deserializer)?;
255                *place = match Self::from_str(s.as_str()) {
256                    Ok(n) => n,
257                    Err(_) => Self::Other(s),
258                };
259                Ok(())
260            }
261        }
262        )*
263    };
264}
265
266impl_serialize_deserialize!(
267    for
268    NSFW,
269    AnimeMediaType,
270    AnimeStatus,
271    UserWatchStatus,
272    AnimeRankingType,
273    MangaRankingType,
274    Season,
275    SortStyle,
276    UserReadStatus,
277    MangaMediaType,
278    MangaStatus,
279    RelationType
280);
281
282impl Serialize for Source {
283    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
284    where
285        S: Serializer,
286    {
287        serializer.serialize_str(self.into())
288    }
289}
290
291impl<'de> Deserialize<'de> for Source {
292    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
293    where
294        D: Deserializer<'de>,
295    {
296        let s = String::deserialize(deserializer)?;
297        match Self::from_str(s.as_str()) {
298            Ok(n) => Ok(n),
299            Err(_) => Ok(Self::Other),
300        }
301    }
302
303    fn deserialize_in_place<D>(
304        deserializer: D,
305        place: &mut Self,
306    ) -> Result<(), <D as Deserializer<'de>>::Error>
307    where
308        D: Deserializer<'de>,
309    {
310        let s = String::deserialize(deserializer)?;
311        *place = match Self::from_str(s.as_str()) {
312            Ok(n) => n,
313            Err(_) => Self::Other,
314        };
315        Ok(())
316    }
317}
318
319impl Serialize for TimeWrapper {
320    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
321    where
322        S: Serializer,
323    {
324        let format = format_description::parse("[hour]:[minute]:[second]").unwrap();
325        serializer.serialize_str(&self.time.format(&format).unwrap())
326    }
327}
328
329impl<'de> Deserialize<'de> for TimeWrapper {
330    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
331    where
332        D: Deserializer<'de>,
333    {
334        use serde::de::Error;
335        let s = String::deserialize(deserializer)?;
336        let re = regex::Regex::new(r"([0-9]+):([0-9]+)").unwrap();
337        if let Some(caps) = re.captures(&s) {
338            let hour = caps.get(1).unwrap();
339            let minute = caps.get(2).unwrap();
340            let hour = match hour.as_str().parse::<u8>() {
341                Ok(hour) => hour,
342                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
343            };
344            let minute = match minute.as_str().parse::<u8>() {
345                Ok(minute) => minute,
346                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
347            };
348            let time = match Time::from_hms(hour, minute, 0) {
349                Ok(time) => time,
350                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
351            };
352            Ok(TimeWrapper { time })
353        } else {
354            Err(D::Error::custom("Could not parse time"))
355        }
356    }
357
358    fn deserialize_in_place<D>(
359        deserializer: D,
360        place: &mut Self,
361    ) -> Result<(), <D as Deserializer<'de>>::Error>
362    where
363        D: Deserializer<'de>,
364    {
365        use serde::de::Error;
366        let s = String::deserialize(deserializer)?;
367        let format = format_description::parse("[hour]:[minute]:[second]").unwrap();
368        if let Ok(time) = Time::parse(&s, &format) {
369            place.time = time;
370            return Ok(());
371        }
372        let re = regex::Regex::new(r"([0-9]+):([0-9]+)").unwrap();
373        if let Some(caps) = re.captures(&s) {
374            let hour = caps.get(1).unwrap();
375            let minute = caps.get(2).unwrap();
376            let hour = match hour.as_str().parse::<u8>() {
377                Ok(hour) => hour,
378                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
379            };
380            let minute = match minute.as_str().parse::<u8>() {
381                Ok(minute) => minute,
382                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
383            };
384            place.time = match Time::from_hms(hour, minute, 0) {
385                Ok(time) => time,
386                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
387            };
388            Ok(())
389        } else {
390            Err(D::Error::custom("Could not parse time"))
391        }
392    }
393}
394
395impl Serialize for DateWrapper {
396    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
397    where
398        S: Serializer,
399    {
400        let format = format_description::parse("[year]-[month]-[day]").unwrap();
401        serializer.serialize_str(&self.date.format(&format).unwrap())
402        // serializer.serialize_str(&self.date.format("%Y-%m-%d"))
403    }
404}
405
406impl<'de> Deserialize<'de> for DateWrapper {
407    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
408    where
409        D: Deserializer<'de>,
410    {
411        use serde::de::Error;
412        let s = String::deserialize(deserializer)?;
413        let format = format_description::parse("[year]-[month]-[day]").unwrap();
414        if let Ok(date) = Date::parse(&s, &format) {
415            return Ok(DateWrapper { date });
416        }
417        let re = regex::Regex::new(r"([0-9]+)-?([0-9]+)?").unwrap();
418        if let Some(caps) = re.captures(&s) {
419            let year = caps.get(1).unwrap();
420            let year = match year.as_str().parse::<i32>() {
421                Ok(year) => year,
422                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
423            };
424            let month = if let Some(month) = caps.get(2) {
425                match month.as_str().parse::<u8>() {
426                    // convert to Month type
427                    Ok(month) => month,
428                    Err(e) => return Err(serde::de::Error::custom(e.to_string())),
429                }
430            } else {
431                1
432            };
433            let date = match Date::from_calendar_date(year, Month::try_from(month).unwrap(), 1) {
434                // TODO: double check Month::try_from
435                Ok(date) => date,
436                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
437            };
438            Ok(DateWrapper { date })
439        } else {
440            Err(D::Error::custom("Could not parse date"))
441        }
442    }
443
444    fn deserialize_in_place<D>(
445        deserializer: D,
446        place: &mut Self,
447    ) -> Result<(), <D as Deserializer<'de>>::Error>
448    where
449        D: Deserializer<'de>,
450    {
451        use serde::de::Error;
452        let s = String::deserialize(deserializer)?;
453        let format = format_description::parse("[year]-[month]-[day]").unwrap();
454        if let Ok(date) = Date::parse(&s, &format) {
455            place.date = date;
456            return Ok(());
457        };
458        let re = regex::Regex::new(r"([0-9]+)-?([0-9]+)?").unwrap();
459        if let Some(caps) = re.captures(&s) {
460            let year = caps.get(1).unwrap();
461            let year = match year.as_str().parse::<i32>() {
462                Ok(year) => year,
463                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
464            };
465            let month = if let Some(month) = caps.get(2) {
466                match month.as_str().parse::<u8>() {
467                    Ok(month) => month,
468                    Err(e) => return Err(serde::de::Error::custom(e.to_string())),
469                }
470            } else {
471                1
472            };
473            place.date = match Date::from_calendar_date(year, Month::try_from(month).unwrap(), 1) {
474                Ok(date) => date,
475                Err(e) => return Err(serde::de::Error::custom(e.to_string())),
476            };
477            Ok(())
478        } else {
479            Err(D::Error::custom("Could not parse date"))
480        }
481    }
482}
483
484impl Serialize for DateTimeWrapper {
485    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
486    where
487        S: Serializer,
488    {
489        serializer.serialize_str(&self.datetime.format(&FORMAT).unwrap())
490    }
491}
492
493impl<'de> Deserialize<'de> for DateTimeWrapper {
494    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
495    where
496        D: Deserializer<'de>,
497    {
498        use serde::de::Error;
499        let s = String::deserialize(deserializer)?;
500        match PrimitiveDateTime::parse(&s, &FORMAT) {
501            Ok(datetime) => Ok(DateTimeWrapper { datetime }),
502            Err(e) => Err(D::Error::custom(e.to_string())),
503        }
504    }
505
506    fn deserialize_in_place<D>(
507        deserializer: D,
508        place: &mut Self,
509    ) -> Result<(), <D as Deserializer<'de>>::Error>
510    where
511        D: Deserializer<'de>,
512    {
513        use serde::de::Error;
514        let s = String::deserialize(deserializer)?;
515        match PrimitiveDateTime::parse(&s, &FORMAT) {
516            Ok(datetime) => {
517                place.datetime = datetime;
518                Ok(())
519            }
520            Err(e) => Err(D::Error::custom(e.to_string())),
521        }
522    }
523}