mal_query/myanimelist/
user.rs

1use std::{error::Error, collections::HashMap};
2use serde::Deserialize;
3use super::{models::*, TOKEN};
4
5#[derive(Debug, Deserialize)]
6pub struct UpdateAnime {
7    id: u32,
8    params: HashMap<String, String>,
9}
10impl UpdateAnime {
11    /// Takes an anime id, and initializes data for an update
12    pub fn new(id: u32) -> Self {
13        UpdateAnime {
14            id,
15            params: HashMap::new(),
16        }
17    }
18    /// Takes an existing MalAnimeData variable, and initializes data for an update
19    pub fn from_malanimedata(mal_data: &MalAnimeData) -> Self {
20        UpdateAnime {
21            id: mal_data.id,
22            params: HashMap::new(),
23        }
24    }
25    /// Calls the MyAnimeList API to update the user's anime entry based on the fields the other methods implement.<br>
26    /// The user must first be logged in, for the app to have received a Token from MyAnimList.<br>
27    /// Example usage:
28    /// ```
29    /// use mal_query::myanimelist::user::UpdateAnime;
30    /// async fn update_example() {
31    ///     let status = UpdateAnime::new(33)
32    ///         .update_score(10)
33    ///         .expect("Score should be between 0-10")
34    ///         .update()
35    ///         .await
36    ///         .unwrap(); // Assuming successful
37    ///     assert_eq!(status.score, 10);
38    /// }
39    /// ```
40    pub async fn update(&mut self) -> Result<ListStatus, Box<dyn Error>> {
41        let url = format!("https://api.myanimelist.net/v2/anime/{}/my_list_status", self.id);
42        let token = TOKEN.lock()?;
43        if token.is_empty() { return Err("User is not logged in")? }
44
45        let client = reqwest::Client::new();
46        let res = client
47            .put(url)
48            .header("Authorization", format!("Bearer {}", *token))
49            .form(&self.params)
50            .send()
51            .await?;
52
53        if res.status().is_success() {
54            let data = res.text().await?;
55            let result: ListStatus = serde_json::from_str(&data)?;
56            return Ok(result);
57        } else {
58            return Err(format!("Request failed with status {:?}", res.status()))?;
59        }
60    }
61    /// Adds an update to the user's status to the Update
62    pub fn update_status(&mut self, new_status: Status) -> &mut Self {
63        let s: &str;
64        match new_status {
65            Status::Completed => s = "completed",
66            Status::Dropped => s = "dropped",
67            Status::OnHold => s = "on_hold",
68            Status::PlanToWatch => s = "plan_to_watch",
69            Status::Watching => s = "watching",
70        }
71        self.params.insert("status".to_string(), s.to_string());
72        self
73    }
74    /// Adds an update to the user's is_rewatching to the Update
75    pub fn update_is_rewatching(&mut self, new_is_rewatching: bool) -> &mut Self {
76        self.params.insert("is_rewatching".to_string(), new_is_rewatching.to_string());
77        self
78    }
79    /// Adds an update to the user's score to the Update
80    pub fn update_score(&mut self, new_score: u32) -> Result<&mut Self, Box<dyn Error>> {
81        if new_score > 10 { return Err("Score has to be 0-10")? }
82        self.params.insert("score".to_string(), new_score.to_string());
83        Ok(self)
84    }
85    /// Adds an update to the user's number of watched episodes to the Update
86    pub fn update_num_watched_episodes(&mut self, new_num_watched_episodes: u32) -> &mut Self {
87        self.params.insert("num_watched_episodes".to_string(), new_num_watched_episodes.to_string());
88        self
89    }
90    /// Adds an update to the user's personal priority to the Update
91    pub fn update_priority(&mut self, new_priority: u32) -> Result<&mut Self, Box<dyn Error>> {
92        if new_priority > 2 { return Err("Priority has to be 0-2")? }
93        self.params.insert("priority".to_string(), new_priority.to_string());
94        Ok(self)
95    }
96    /// Adds an update to the user's number of times rewatched to the Update
97    pub fn update_num_times_rewatched(&mut self, new_num_times_rewatched: u32) -> &mut Self {
98        self.params.insert("num_times_rewatched".to_string(), new_num_times_rewatched.to_string());
99        self
100    }
101    /// Adds an update to the user's rewatch value to the Update
102    pub fn update_rewatch_value(&mut self, new_rewatch_value: u32) -> Result<&mut Self, Box<dyn Error>> {
103        if new_rewatch_value > 5 { return Err("rewatch_value has to be 0-5")? }
104        self.params.insert("rewatch_value".to_string(), new_rewatch_value.to_string());
105        Ok(self)
106    }
107    /// Overrides the tags of the user's anime entry<br>
108    /// ### WARNING:<br> 
109    /// This will change all tags into ONLY what the params are
110    pub fn update_tags(&mut self, new_tags: Vec<&str>) -> &mut Self {
111        self.params.insert("tags".to_string(), new_tags.join(","));
112        self
113    }
114    /// Overrides the comment of the user's anime entry
115    pub fn update_comments(&mut self, new_comments: &str) -> &mut Self {
116        self.params.insert("comments".to_string(), new_comments.to_string());
117        self
118    }
119    /// Changes the Starting Date of the user's entry
120    /// ### WARNING:<br> 
121    /// A date beyond today's date given to the MyAnimeList API will simply ignore the parameter.
122    pub fn update_start_date(&mut self, new_year: u32, new_month: u32, new_day: u32) -> &mut Self {
123        let new_start_date = format!("{:04}-{:02}-{:02}", new_year, new_month, new_day);
124        self.params.insert("start_date".to_string(), new_start_date.to_string());
125        self
126    }
127    /// Changes the Finish Date of the user's entry
128    /// ### WARNING:<br> 
129    /// An date beyond today's date given to the MyAnimeList API will simply ignore the parameter.
130    pub fn update_finish_date(&mut self, new_year: u32, new_month: u32, new_day: u32) -> &mut Self {
131        let new_finish_date = format!("{:04}-{:02}-{:02}", new_year, new_month, new_day);
132        self.params.insert("finish_date".to_string(), new_finish_date.to_string());
133        self
134    }
135}
136
137/// Deletes an anime of the corresponding ID from the User's MyAnimeList database.<br>
138/// User MUST be loggin in with the `login` function, and have a token generated for them for this to be used.
139pub async fn delete_anime(id: u32) -> Result<(), Box<dyn Error>> {
140    let url = format!("https://api.myanimelist.net/v2/anime/{id}/my_list_status");
141    let token = TOKEN.lock()?;
142    if token.is_empty() { return Err("User is not logged in")? }
143
144    let client = reqwest::Client::new();
145    let res = client
146        .delete(url)
147        .header("Authorization", format!("Bearer {}", *token))
148        .send()
149        .await?;
150
151    match res.status().is_success() {
152        true => Ok(()),
153        false => Err(format!("Request failed with status {:?}", res.status()))?,
154    }
155}