11use std:: {
22 fs,
3- io,
3+ io:: {
4+ self ,
5+ } ,
46 path:: PathBuf ,
57} ;
68
79use serde:: Deserialize ;
810
911#[ derive( Deserialize , Debug , Clone ) ]
10- pub struct Config {
11- home_dir : Option < PathBuf > ,
12- config_dir : Option < PathBuf > ,
13- config_file : Option < PathBuf > ,
14- pub leetcode_token : Option < String > ,
12+ pub struct ConfigFile {
13+ pub leetcode_token : String ,
1514 pub default_language : Option < String > ,
1615 pub leetcode_dir_path : Option < PathBuf > ,
1716}
1817
19- impl Config {
18+ #[ derive( Deserialize , Debug , Clone ) ]
19+ pub struct RuntimeConfigSetup {
20+ pub home_dir : PathBuf ,
21+ pub config_dir : PathBuf ,
22+ pub config_file : PathBuf ,
23+ pub config : ConfigFile ,
24+ }
25+
26+ impl RuntimeConfigSetup {
2027 pub fn new ( ) -> Self {
2128 let home_dir =
2229 dirs:: home_dir ( ) . expect ( "Unable to determine home directory" ) ;
2330 let config_dir = home_dir. join ( ".config/leetcode-cli" ) ;
2431 let config_file = config_dir. join ( "config.toml" ) ;
2532 let default_leetcode_dir = home_dir. join ( "leetcode" ) ;
2633
27- Config {
28- home_dir : Some ( home_dir) ,
29- config_dir : Some ( config_dir) ,
30- config_file : Some ( config_file) ,
31- leetcode_token : None ,
32- default_language : None ,
33- leetcode_dir_path : Some ( default_leetcode_dir) ,
34+ RuntimeConfigSetup {
35+ home_dir,
36+ config_dir,
37+ config_file,
38+ config : ConfigFile {
39+ leetcode_token : String :: new ( ) ,
40+ default_language : None ,
41+ leetcode_dir_path : Some ( default_leetcode_dir) ,
42+ } ,
3443 }
3544 }
3645 /// create a config file in ~/.config/leetcode-cli/config.toml
3746 fn create_config_file ( & self ) {
38- let config_dir =
39- self . config_dir . as_ref ( ) . expect ( "Config directory not set" ) ;
40-
41- std:: fs:: create_dir_all ( & config_dir)
47+ std:: fs:: create_dir_all ( & self . config_dir )
4248 . expect ( "Unable to create directory" ) ;
43- let config_file = config_dir. join ( "config.toml" ) ;
49+ let config_file = self . config_dir . join ( "config.toml" ) ;
4450 std:: fs:: File :: create ( & config_file) . expect ( "Unable to create file" ) ;
4551 }
4652 /// check for a config file in ~/.config/leetcode-cli/config.toml
47- /// read it or create it if it doesn't exist with default values
53+ /// read it or create it with default values if it doesn't exist
4854 /// load the config in Config struct and check if the token is valid
49- /// if the token is not valid, generate a new one
50- pub async fn status ( & mut self ) -> Result < ( ) , reqwest:: Error > {
51- if self . config_file . is_some ( ) {
52- let config_file =
53- std:: fs:: read_to_string ( self . config_file . as_ref ( ) . unwrap ( ) )
54- . expect ( "Unable to read file" ) ;
55- let config: Config =
56- toml:: from_str ( & config_file) . expect ( "Unable to parse file" ) ;
57- self . leetcode_token = config. leetcode_token ;
58- self . default_language = config. default_language ;
59- if let Some ( custom) = config. leetcode_dir_path {
60- self . leetcode_dir_path = Some ( PathBuf :: from ( custom) ) ;
55+ pub fn status ( & mut self ) -> Result < ( ) , io:: Error > {
56+ if self . config_file . is_file ( ) {
57+ let config_file = std:: fs:: read_to_string ( & self . config_file )
58+ . expect ( "Unable to read file" ) ;
59+ let parsed_config: ConfigFile =
60+ toml:: from_str ( & config_file) . expect ( "Unable to parse config" ) ;
61+ if !parsed_config. leetcode_token . is_empty ( ) {
62+ self . config . leetcode_token = parsed_config. leetcode_token ;
63+ }
64+ if parsed_config. default_language . is_some ( ) {
65+ self . config . default_language = parsed_config. default_language ;
66+ }
67+ if parsed_config. leetcode_dir_path . is_some ( ) {
68+ self . config . leetcode_dir_path = parsed_config. leetcode_dir_path ;
6169 }
62- self . check_token ( ) . await ?;
6370 } else {
6471 self . create_config_file ( ) ;
65- self . generate_token ( ) ;
6672 }
6773 Ok ( ( ) )
6874 }
6975
70- async fn test_token_validity ( & self ) -> Result < bool , reqwest:: Error > {
71- let token = self . leetcode_token . as_ref ( ) . unwrap ( ) ;
72- let url = format ! (
73- "https://leetcode.com/api/user/check_token/?token={}" ,
74- token
75- ) ;
76- let response = reqwest:: get ( & url) . await . unwrap ( ) ;
77- if response. status ( ) . is_success ( ) {
78- return Ok ( true ) ;
79- }
80- Ok ( false )
81- }
82-
83- async fn check_token ( & mut self ) -> Result < bool , reqwest:: Error > {
84- let token = self . leetcode_token . as_ref ( ) ;
85- if token. is_none ( ) {
86- println ! ( "Leetcode token not found" ) ;
87- println ! ( "Let's set it together." ) ;
88- self . generate_token ( ) ;
89- return Ok ( true ) ;
90- }
91- self . test_token_validity ( ) . await
92- }
93-
94- /// Generate a new LeetCode token by prompting the user to visit the
95- /// LeetCode login page and copy the token
96- fn generate_token ( & mut self ) {
97- unimplemented ! (
98- "generating token is not implemented yet. Refer to the README for \
99- instructions."
100- ) ;
101- println ! ( "Generating token..." ) ;
102- println ! ( "Please visit https://leetcode.com/submissions/#/1" ) ;
103- println ! ( "and copy the token here:" ) ;
104- let mut token = String :: new ( ) ;
105- std:: io:: stdin ( )
106- . read_line ( & mut token)
107- . expect ( "Failed to read line" ) ;
108- let token = token. trim ( ) ;
109- if token. is_empty ( ) {
110- println ! ( "Token cannot be empty" ) ;
111- return ;
112- }
113- self . leetcode_token = Some ( token. to_string ( ) ) ;
114- println ! ( "Token set to: {}" , token) ;
115- }
116-
11776 /// Resolve the configured LeetCode directory, expand ~, canonicalize, and
11877 /// create if missing.
11978 pub fn resolve_leetcode_dir ( & self ) -> io:: Result < PathBuf > {
120- let raw = if let Some ( ref custom) = self . leetcode_dir_path {
79+ let raw = if let Some ( ref custom) = self . config . leetcode_dir_path {
12180 custom. to_string_lossy ( )
12281 } else {
12382 return Err ( io:: Error :: new (
@@ -127,22 +86,14 @@ impl Config {
12786 } ;
12887 let raw_str = raw. as_ref ( ) ;
12988 let mut path = if raw_str == "~" {
130- self . home_dir . clone ( ) . ok_or ( io:: Error :: new (
131- io:: ErrorKind :: NotFound ,
132- "Unable to find home directory" ,
133- ) ) ?
89+ self . home_dir . clone ( )
13490 } else if raw_str. starts_with ( "~/" ) {
135- let home = dirs:: home_dir ( ) . ok_or ( io:: Error :: new (
136- io:: ErrorKind :: NotFound ,
137- "Unable to find home directory" ,
138- ) ) ?;
91+ let home = self . home_dir . clone ( ) ;
13992 home. join ( & raw_str[ 2 ..] )
14093 } else {
14194 PathBuf :: from ( raw_str)
14295 } ;
143- // Canonicalize parent to normalize .. and . components
14496 path = fs:: canonicalize ( & path) . unwrap_or ( path) ;
145- // Ensure directory exists
14697 fs:: create_dir_all ( & path) ?;
14798 Ok ( path)
14899 }
0 commit comments