mal_query/myanimelist/
builders.rs

1use std::error::Error;
2
3use super::{*, models::{MalAnimeData, Season, Status, Sort}};
4
5pub struct Builder {
6    url: String,
7}
8impl Builder {
9    /// Takes an anime ID, and initializes a single entry retriever for the corresponding anime
10    pub fn new(id: u32) -> Self {
11        Builder {
12            url: format!("https://api.myanimelist.net/v2/anime/{id}?fields=").to_string(),
13        }
14    }
15    /// Calls the MyAnimeList API to recieve anime created by the builder, based on the ID 
16    /// and fields added from the other methods.<br>
17    /// The user does not need to be logged in, aside from using the `.add_my_list_status()` 
18    /// to the result. <br>
19    /// This method returns a Result, containing either the data in a `MalAnimeData`, or an error.
20    /// ### Example usage:
21    /// ```
22    /// use mal_query::myanimelist::builders::{Builder, AddFields};
23    /// async fn builder_example() {
24    ///     let berserk = Builder::new(33)
25    ///         .add_status()
26    ///         .add_num_episodes()
27    ///         .run()
28    ///         .await;
29    ///     match berserk {
30    ///         Err(_e) => assert!(false),
31    ///         Ok(data) => {
32    ///             assert!(
33    ///                 data
34    ///                 .title
35    ///                 .to_lowercase()
36    ///                 .contains("berserk")
37    ///             );
38    ///             assert_eq!(data.id, 33);
39    ///         }
40    ///     }
41    /// }
42    /// ```
43    pub async fn run(&self) -> Result<MalAnimeData, Box<dyn Error>> {
44        run_get(&self.url).await
45    }
46}
47
48pub struct SearchBuilder {
49    url: String,
50}
51impl SearchBuilder {
52    /// Takes an anime name and limiter, and initializes a search entry retriever for the corresponding anime
53    pub fn new(name: &str, limit: u32) -> Self {
54        SearchBuilder {
55            url: format!("https://api.myanimelist.net/v2/anime?q={name}&limit={limit}&fields=").to_string(),
56        }
57    }
58    /// Calls the MyAnimeList API to recieve anime created by the builder, based on the name, a limiter, 
59    /// and fields added from the other methods.<br>
60    /// The user does not need to be logged in, aside from using the `.add_my_list_status()` 
61    /// to the result. <br>
62    /// This method returns a Result, containing either the data in a `MalAnimeSearch`, or an error.
63    /// ### Example usage:
64    /// ```
65    /// use mal_query::myanimelist::builders::{SearchBuilder, AddFields};
66    /// async fn search_builder_example() {
67    ///     let berserk = SearchBuilder::new("berserk", 1)
68    ///         .add_start_date()
69    ///         .add_rank()
70    ///         .run()
71    ///         .await;
72    ///     match berserk {
73    ///         Err(_e) => assert!(false),
74    ///         Ok(data_vec) => {
75    ///             let data = data_vec.get(0).unwrap();
76    ///             assert!(
77    ///                 data
78    ///                 .title
79    ///                 .to_lowercase()
80    ///                 .contains("berserk")
81    ///             );
82    ///             assert_eq!(data.id, 33);
83    ///         }
84    ///     }
85    /// }
86    /// ```
87    pub async fn run(&self) -> Result<MalAnimeSearch, Box<dyn Error>> {
88        run_search(&self.url).await
89    }
90}
91
92pub struct SeasonalBuilder {
93    url: String,
94}
95impl SeasonalBuilder {
96    /// Takes a year and `Season`, and initializes a seasonal search entry retriever for the corresponding anime
97    pub fn new(year: u32, season: Season) -> Self {
98        let s: &str;
99        match season {
100            Season::Winter => s = "winter",
101            Season::Spring => s = "spring",
102            Season::Summer => s = "summer",
103            Season::Fall => s = "fall"
104        }
105        SeasonalBuilder {
106            url: format!("https://api.myanimelist.net/v2/anime/season/{year}/{s}?fields=").to_string(),
107        }
108    }
109    /// Calls the MyAnimeList API to recieve anime created by the builder, based on the year, the season, 
110    /// and fields added from the other methods.<br>
111    /// The user does not need to be logged in, aside from using the `.add_my_list_status()` 
112    /// to the result. <br>
113    /// This method returns a Result, containing either the data in a `MalAnimeSearch`, or an error.
114    /// ### Example usage:
115    /// ```
116    /// use mal_query::myanimelist::builders::{SeasonalBuilder, AddFields};
117    /// use mal_query::myanimelist::models::Season;
118    /// async fn seasonal_builder_example() {
119    ///     let winter_2023 = SeasonalBuilder::new(2023, Season::Winter)
120    ///         .add_start_season()
121    ///         .add_start_date()
122    ///         .run()
123    ///         .await;
124    ///     match winter_2023 {
125    ///         Err(_e) => assert!(false),
126    ///         Ok(data_vec) => assert!(true),
127    ///     }
128    /// }
129    /// ```
130    pub async fn run(&self) -> Result<MalAnimeSearch, Box<dyn Error>> {
131        run_search(&self.url).await
132    }
133}
134
135pub struct UserListBuilder {
136    url: String,
137}
138impl UserListBuilder {
139    /// Takes a MyAnimeList username, and initializes a user's animelist retriever
140    pub fn new(username: &str) -> Self {
141        UserListBuilder {
142            url: format!("https://api.myanimelist.net/v2/users/{username}/animelist?")
143        }
144    }
145    /// A filter added to UserListBuilder that will tell the `run()` to filter by the user's listed status
146    pub fn status(&mut self, status: Status) -> &mut Self {
147        let s: &str;
148        match status {
149            Status::Completed => s = "completed",
150            Status::Dropped => s = "dropped",
151            Status::OnHold => s = "on_hold",
152            Status::PlanToWatch => s = "plan_to_watch",
153            Status::Watching => s = "watching",
154        }
155        self.url.push_str(&format!("status={s}&"));
156        self
157    }
158    /// A filter added to UserListBuilder that will tell the `run()` to sort the User's list
159    pub fn sort(&mut self, sort: Sort) -> &mut Self {
160        let s: &str;
161        match sort {
162            Sort::AnimeId => s = "anime_id",
163            Sort::AnimeStartDate => s = "anime_start_date",
164            Sort::AnimeTitle => s = "anime_title",
165            Sort::ListScore => s = "list_score",
166            Sort::ListUpdatedAt => s = "list_updated_at"
167        }
168        self.url.push_str(&format!("sort={s}&"));
169        self
170    }
171    /// A filter added to UserListBuilder that will tell the `run()` to limit the entries in the User's list
172    pub fn limit(&mut self, limit: u32) -> &mut Self {
173        self.url.push_str(&format!("limit={limit}&"));
174        self
175    }
176    /// A filter added to UserListBuilder that will tell the `run()` to offset the starting point of the user's list.
177    /// For example, if the limit is 10 for a first `run()`, and you want the 10 afterwards, you'd add `.offset(10)`
178    pub fn offset(&mut self, offset: u32) -> &mut Self {
179        self.url.push_str(&format!("offset={offset}&"));
180        self
181    }
182    /// A field added to UserListBuilder that will tell the `run()` to add the user's list details
183    pub fn include_list_status(&mut self) -> &mut Self {
184        self.url.push_str("fields=list_status{{is_rewatching,num_times_rewatched,rewatch_value,priority,tags,comments,start_date,end_date}}&");
185        self
186    }
187    /// Calls the MyAnimeList API to recieve a user's animelist created by the builder, based on the username, 
188    /// and fields added from the other methods.<br>
189    /// The user does not need to be logged in, aside from using the `.add_my_list_status()` 
190    /// to the result. The user searched must also not be private<br>
191    /// This method returns a Result, containing either the data in a `MalAnimeSearch`, or an error.
192    /// Example usage:
193    /// ```
194    /// use mal_query::myanimelist::builders::UserListBuilder;
195    /// use mal_query::myanimelist::models::Status;
196    /// async fn user_list_builder_example() {
197    ///     let api = UserListBuilder::new("naginis_api")
198    ///         .limit(10)
199    ///         .status(Status::Watching)
200    ///         .run()
201    ///         .await;
202    ///     match api {
203    ///         Err(_e) => assert!(false),
204    ///         Ok(data_vec) => assert!(true),
205    ///     }
206    /// }
207    /// ```
208    pub async fn run(&self) -> Result<MalAnimeSearch, Box<dyn Error>> {
209        run_search(&self.url).await
210    }
211    // TODO: feature: add Builder addon, to include more data
212}
213
214pub trait AddFields {
215    fn add_id(&mut self) -> &mut Self;
216    fn add_title(&mut self) -> &mut Self;
217    fn add_main_picture(&mut self) -> &mut Self;
218    fn add_alt_titles(&mut self) -> &mut Self;
219    fn add_start_date(&mut self) -> &mut Self;
220    fn add_end_date(&mut self) -> &mut Self;
221    fn add_synopsis(&mut self) -> &mut Self;
222    fn add_mean(&mut self) -> &mut Self;
223    fn add_rank(&mut self) -> &mut Self;
224    fn add_popularity(&mut self) -> &mut Self;
225    fn add_num_list_users(&mut self) -> &mut Self;
226    fn add_num_scoring_users(&mut self) -> &mut Self;
227    fn add_nsfw(&mut self) -> &mut Self;
228    fn add_created_at(&mut self) -> &mut Self;
229    fn add_updated_at(&mut self) -> &mut Self;
230    fn add_media_type(&mut self) -> &mut Self;
231    fn add_status(&mut self) -> &mut Self;
232    fn add_genres(&mut self) -> &mut Self;
233    fn add_my_list_status(&mut self) -> &mut Self;
234    fn add_start_season(&mut self) -> &mut Self;
235    fn add_num_episodes(&mut self) -> &mut Self;
236    fn add_broadcast(&mut self) -> &mut Self;
237    fn add_source(&mut self) -> &mut Self;
238    fn add_average_episode_duration(&mut self) -> &mut Self;
239    fn add_rating(&mut self) -> &mut Self;
240    fn add_pictures(&mut self) -> &mut Self;
241    fn add_background(&mut self) -> &mut Self;
242    fn add_related_anime(&mut self) -> &mut Self;
243    fn add_related_manga(&mut self) -> &mut Self;
244    fn add_recommendations(&mut self) -> &mut Self;
245    fn add_studios(&mut self) -> &mut Self;
246    fn add_statistics(&mut self) -> &mut Self;
247}
248
249// ChatGPT taught me that I can do this, a macro to add the methods to each struct.
250// Figured I'd rather have it one once than like, this times 3
251macro_rules! impl_filters_for_builders {
252    ($($struct:ident),*) => {
253        $(
254            impl AddFields for $struct {
255                /// Adds the option of an Id to be given to the final result
256                fn add_id(&mut self) -> &mut Self {
257                    self.url.push_str("id,");
258                    self
259                }
260                /// Adds the option of a title to be given to the final result
261                fn add_title(&mut self) -> &mut Self {
262                    self.url.push_str("title,");
263                    self
264                }
265                /// Adds the option of a Main Picture to be given to the final result
266                fn add_main_picture(&mut self) -> &mut Self {
267                    self.url.push_str("main_picture,");
268                    self
269                }
270                /// Adds the option of Alternate Titles to be given to the final result
271                fn add_alt_titles(&mut self) -> &mut Self {
272                    self.url.push_str("alternative_titles,");
273                    self
274                }
275                /// Adds the option of a Start Date to be given to the final result
276                fn add_start_date(&mut self) -> &mut Self {
277                    self.url.push_str("start_date,");
278                    self
279                }
280                /// Adds the option of an End Date to be given to the final result
281                fn add_end_date(&mut self) -> &mut Self {
282                    self.url.push_str("end_date,");
283                    self
284                }
285                /// Adds the option of a Synopsis to be given to the final result
286                fn add_synopsis(&mut self) -> &mut Self {
287                    self.url.push_str("synopsis,");
288                    self
289                }
290                /// Adds the option of a Mean to be given to the final result
291                fn add_mean(&mut self) -> &mut Self {
292                    self.url.push_str("mean,");
293                    self
294                }
295                /// Adds the option of a Rank to be given to the final result
296                fn add_rank(&mut self) -> &mut Self {
297                    self.url.push_str("rank,");
298                    self
299                }
300                /// Adds the option of Popularity to be given to the final result
301                fn add_popularity(&mut self) -> &mut Self {
302                    self.url.push_str("popularity,");
303                    self
304                }
305                /// Adds the option of Num. List Users to be given to the final result
306                fn add_num_list_users(&mut self) -> &mut Self {
307                    self.url.push_str("num_list_users,");
308                    self
309                }
310                /// Adds the option of Num. Scoring Users to be given to the final result
311                fn add_num_scoring_users(&mut self) -> &mut Self {
312                    self.url.push_str("num_scoring_users,");
313                    self
314                }
315                /// Adds the option of NSFW rating to be given to the final result
316                fn add_nsfw(&mut self) -> &mut Self {
317                    self.url.push_str("nsfw,");
318                    self
319                }
320                /// Adds the option of a Created Date to be given to the final result
321                fn add_created_at(&mut self) -> &mut Self {
322                    self.url.push_str("created_at,");
323                    self
324                }
325                /// Adds the option of an Updated Date to be given to the final result
326                fn add_updated_at(&mut self) -> &mut Self {
327                    self.url.push_str("updated_at,");
328                    self
329                }
330                /// Adds the option of a Media Type to be given to the final result
331                fn add_media_type(&mut self) -> &mut Self {
332                    self.url.push_str("media_type,");
333                    self
334                }
335                /// Adds the option of the Airing Status to be given to the final result
336                fn add_status(&mut self) -> &mut Self {
337                    self.url.push_str("status,");
338                    self
339                }
340                /// Adds the option of Genres to be given to the final result
341                fn add_genres(&mut self) -> &mut Self {
342                    self.url.push_str("genres,");
343                    self
344                }
345                /// Adds the option of your List Status to be given to the final result
346                fn add_my_list_status(&mut self) -> &mut Self {
347                    self.url.push_str("my_list_status,");
348                    self
349                }
350                /// Adds the option of Start Season data to be given to the final result
351                fn add_start_season(&mut self) -> &mut Self {
352                    self.url.push_str("start_season,");
353                    self
354                }
355                /// Adds the option of an Episode Count to be given to the final result
356                fn add_num_episodes(&mut self) -> &mut Self {
357                    self.url.push_str("num_episodes,");
358                    self
359                }
360                /// Adds the option of Broadcast data to be given to the final result
361                fn add_broadcast(&mut self) -> &mut Self {
362                    self.url.push_str("broadcast,");
363                    self
364                }
365                /// Adds the option of Source data to be given to the final result
366                fn add_source(&mut self) -> &mut Self {
367                    self.url.push_str("source,");
368                    self
369                }
370                /// Adds the option of an Average Episode Duration to be given to the final result
371                fn add_average_episode_duration(&mut self) -> &mut Self {
372                    self.url.push_str("average_episode_duration,");
373                    self
374                }
375                /// Adds the option of a Rating type to be given to the final result
376                fn add_rating(&mut self) -> &mut Self {
377                    self.url.push_str("rating,");
378                    self
379                }
380                /// Adds the option of MyAnimeList's Pictures to be given to the final result
381                fn add_pictures(&mut self) -> &mut Self {
382                    self.url.push_str("pictures,");
383                    self
384                }
385                /// Adds the option of the show's Background to be given to the final result
386                fn add_background(&mut self) -> &mut Self {
387                    self.url.push_str("background,");
388                    self
389                }
390                /// Adds the option of Related Anime to be given to the final result
391                fn add_related_anime(&mut self) -> &mut Self {
392                    self.url.push_str("related_anime,");
393                    self
394                }
395                /// Adds the option of Related Manga to be given to the final result
396                fn add_related_manga(&mut self) -> &mut Self {
397                    self.url.push_str("related_manga,");
398                    self
399                }
400                /// Adds the option of Recommendations to be given to the final result
401                fn add_recommendations(&mut self) -> &mut Self {
402                    self.url.push_str("recommendations,");
403                    self
404                }
405                /// Adds the option of Studio data to be given to the final result
406                fn add_studios(&mut self) -> &mut Self {
407                    self.url.push_str("studios,");
408                    self
409                }
410                /// Adds the option of MyAnimeList Statistics to be given to the final result
411                fn add_statistics(&mut self) -> &mut Self {
412                    self.url.push_str("statistics,");
413                    self
414                }
415            }
416        )*
417    };
418}
419
420impl_filters_for_builders!(Builder, SearchBuilder, SeasonalBuilder);