mal/config/
oauth_config.rs1use super::*;
2use figlet_rs::FIGfont;
3use serde::{Deserialize, Serialize};
4use serde_yaml;
5use std::{
6 fs,
7 io::{stdin, Write},
8 path::Path,
9};
10#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
11pub struct AuthConfig {
12 pub client_id: String,
13 pub user_agent: Option<String>,
14 pub port: Option<u16>,
15}
16
17impl AuthConfig {
18 pub fn load() -> Result<Self, ConfigError> {
20 let paths = Self::get_paths()?;
21 if paths.config_file_path.exists() {
22 let config_string = fs::read_to_string(&paths.config_file_path)?;
23 let config_yml: AuthConfig = serde_yaml::from_str(&config_string)?;
24
25 Ok(config_yml)
26 } else {
27 let standard_font = FIGfont::standard().unwrap();
28 let figlet = standard_font.convert("MAL-CLI");
29 let banner = figlet.unwrap().to_string();
30 println!("{}", banner);
31 println!(
32 "Config will be saved to {}",
33 paths.config_file_path.display()
34 );
35
36 println!("\nHow to get setup:\n");
37
38 let instructions = [
39 "Go to the myanimelist api page - https://myanimelist.net/apiconfig",
40 "Click `Create ID` and create an app",
41 &format!(
42 "Add `http://127.0.0.1:{}` to the Redirect URIs",
43 DEFAULT_PORT
44 ),
45 "You are now ready to authenticate with myanimelist!",
46 ];
47
48 let mut number = 1;
49 for item in instructions.iter() {
50 println!(" {}. {}", number, item);
51 number += 1;
52 }
53
54 let mut client_id = String::new();
55 loop {
56 println!("\nEnter your client ID: ");
57 stdin().read_line(&mut client_id)?;
58 let trimmed_client_id = client_id.trim().to_string();
59 if trimmed_client_id.len() == 32
60 && trimmed_client_id.chars().all(|c| c.is_ascii_hexdigit())
61 {
62 client_id = trimmed_client_id;
63 break;
64 } else {
65 println!("Invalid client ID format. try again");
66 client_id.clear();
67 }
68 }
69 let mut user_agent = String::new();
70 println!("\nEnter User Agent (default {}): ", DEFAULT_USER_AGENT);
71 stdin().read_line(&mut user_agent)?;
72
73 let user_agent = match user_agent.trim().len() {
74 0 => DEFAULT_USER_AGENT.to_string(),
75 _ => user_agent.trim().to_string(),
76 };
77
78 let mut port = String::new();
79 println!("\nEnter port of redirect uri (default {}): ", DEFAULT_PORT);
80 stdin().read_line(&mut port)?;
81 let port = port.trim().parse::<u16>().unwrap_or(DEFAULT_PORT);
82
83 let config_yml = AuthConfig {
84 client_id,
85 user_agent: Some(user_agent),
86 port: Some(port),
87 };
88
89 let content_yml = serde_yaml::to_string(&config_yml)?;
90
91 let mut new_config = fs::File::create(&paths.config_file_path)?;
92 write!(new_config, "{}", content_yml)?;
93
94 Ok(config_yml)
95 }
96 }
97
98 pub fn get_redirect_uri(&self) -> String {
99 format!("127.0.0.1:{}", self.get_port())
100 }
101
102 pub fn get_port(&self) -> u16 {
103 self.port.unwrap_or(DEFAULT_PORT)
104 }
105
106 pub fn get_user_agent(&self) -> String {
107 match &self.user_agent {
108 Some(s) => s.clone(),
109 None => DEFAULT_USER_AGENT.to_string(),
110 }
111 }
112
113 pub fn get_paths() -> Result<ConfigPaths, ConfigError> {
114 match dirs::home_dir() {
115 Some(home) => {
116 let path = Path::new(&home);
117 let home_config_dir = path.join(CONFIG_DIR);
118 let app_config_dir = home_config_dir.join(APP_CONFIG_DIR);
119
120 if !home_config_dir.exists() {
121 fs::create_dir(&home_config_dir)?;
122 }
123
124 if !app_config_dir.exists() {
125 fs::create_dir(&app_config_dir)?;
126 }
127
128 let config_file_path = &app_config_dir.join(OAUTH_FILE);
129 let token_cache_path = &app_config_dir.join(TOKEN_CACHE_FILE);
130
131 let paths = ConfigPaths {
132 config_file_path: config_file_path.to_path_buf(),
133 auth_cache_path: token_cache_path.to_path_buf(),
134 };
135
136 Ok(paths)
137 }
138 None => Err(ConfigError::PathError),
139 }
140 }
141}