1use std::error::Error;
2
3use inquire::formatter::OptionFormatter;
4use inquire::Select;
5use reqwest::header::AUTHORIZATION;
6use serde_json::Value;
7
8use crate::{
9 auth::{get_access_token, reauthenticate},
10 mal::{Anime, AnimeList},
11};
12
13pub struct ListArgs {
14 num: Option<i32>,
15}
16
17impl ListArgs {
18 pub fn new(num: Option<i32>) -> Self {
19 Self { num }
20 }
21}
22
23pub async fn show_list(args: ListArgs) -> Result<(), Box<dyn Error + Send>> {
24 let access_token = get_access_token().await.unwrap();
25 let client = reqwest::Client::new();
26
27 let num_anime = match args.num {
28 Some(num) => num,
29 _ => 10,
30 };
31 let response = client
33 .get(format!("https://api.myanimelist.net/v2/users/@me/animelist?fields=list_status&limit={num_anime}"))
34 .header(AUTHORIZATION, format!("Bearer {}", &access_token))
35 .send()
36 .await
37 .unwrap();
38
39 if response.status().is_success() {
40 let response_text = response.text().await.unwrap();
41 let response_json: Value = serde_json::from_str(response_text.as_str()).unwrap();
42
43 let formatter: OptionFormatter<AnimeList> =
44 &|i| format!("\nSelected - {}", (*i.value).clone().get_title());
45 loop {
46 let anime_list = parse_anime_list_from_json(&response_json);
47 let anime = Select::new("Status\tCompleted\tTitle", anime_list)
48 .with_page_size(10)
49 .with_formatter(formatter)
50 .prompt();
51
52 match anime {
53 Ok(anime) => show_anime_details(anime.get_id()).await,
54 Err(_) => {
55 break;
56 }
57 }
58 }
59 } else {
60 let _ = reauthenticate().await;
62 println!("Reauthenticated token. Please try again...")
63 }
64 Ok(())
65}
66
67pub fn parse_anime_list_from_json(json: &Value) -> Vec<AnimeList> {
68 let data: &Value = json.get("data").unwrap();
69
70 data.as_array()
71 .unwrap()
72 .iter()
73 .map(|anime| AnimeList::new(anime))
74 .collect::<Vec<AnimeList>>()
75}
76
77pub async fn show_anime_details(id: i64) {
78 let access_token = get_access_token().await.unwrap();
82 let client = reqwest::Client::new();
83 let response = client
84 .get(
85 format!(
86 "https://api.myanimelist.net/v2/anime/{}?fields=id,title,start_date,end_date,synopsis,mean,rank",
87 id
88 )
89 )
90 .header(AUTHORIZATION, format!("Bearer {}", &access_token))
91 .send()
92 .await
93 .unwrap();
94
95 if response.status().is_success() {
96 let response_text = response.text().await.unwrap();
97 let anime: Anime = serde_json::from_str(&response_text).unwrap();
98 println!("{}", anime);
99 }
100
101 let options: Result<String, inquire::InquireError> = Select::new(
102 "Options",
103 vec!["Go back".to_string(), "Open MAL page".to_string()],
104 )
105 .prompt();
106
107 match options {
108 Ok(choice) => {
109 if choice == "Open MAL page" {
110 open_url(id);
111 }
112 }
113 _ => (),
114 }
115}
116
117pub fn open_url(id: i64) {
118 open::that(format!("https://myanimelist.net/anime/{}", id)).unwrap();
119}