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);