From 3554308fb80776078657328c28288e3c6e50cce0 Mon Sep 17 00:00:00 2001 From: dfayd <78728332+dfayd0@users.noreply.github.com> Date: Thu, 29 May 2025 14:51:08 +0200 Subject: [PATCH 1/9] refactor(lib): passing from only main.r to main consuming its lib --- Cargo.toml | 12 ++++++++++-- src/lib.rs | 11 +++++++++++ src/main.rs | 14 +++++--------- 3 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4158a42..d97a5dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ name = "leetcode-cli" version = "0.1.0" authors = ["Coding Kelps"] -autotests = false categories = ["command-line-utilities"] description = "Interact with LeetCode in your development environment." edition = "2021" @@ -14,16 +13,25 @@ readme = "README.md" repository = "https://github.com/coding-kelps/leetcode-cli" rust-version = "1.83" +[[bin]] +name = "leetcode_cli" +path = "src/main.rs" + +[lib] +name = "leetcode_cli" +path = "src/lib.rs" + [dependencies] leetcoderustapi = "1.0.8" clap = { version = "4.5.34", features = ["derive"] } toml = "0.8.20" toml_edit = "0.22.24" serde = { version = "1.0.219", features = ["serde_derive"] } -reqwest = "0.12.15" +reqwest = "0.12.18" tokio = { version = "1.44.1", features = ["full"] } dirs = "6.0.0" warp = "0.3.7" colored = "3.0.0" nanohtml2text = "0.2.1" html2md = "0.2.15" +tempfile = "3.20.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..eff6465 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +pub mod cli; +pub mod config; +pub mod leetcode_api_runner; +pub mod utils; + +pub use cli::{ + Cli, + Commands, +}; +pub use config::Config; +pub use leetcode_api_runner::LeetcodeApiRunner; diff --git a/src/main.rs b/src/main.rs index 0de0538..c332908 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,17 @@ -mod cli; -mod config; -mod leetcode_api_runner; -mod utils; - use clap::Parser; -use cli::{ +use leetcode_cli::{ + utils, Cli, Commands, + Config, + LeetcodeApiRunner, }; -use config::Config; -use leetcode_api_runner::LeetcodeApiRunner; #[tokio::main] async fn main() -> Result<(), Box> { let cli = Cli::parse(); let mut config = Config::new(); - config.status().await?; + config.status()?; let api_runner = LeetcodeApiRunner::new(&mut config).await; match &cli.command { From fbbc13b83d4d0a538387db46be6b7ee66f76e493 Mon Sep 17 00:00:00 2001 From: dfayd <78728332+dfayd0@users.noreply.github.com> Date: Thu, 29 May 2025 14:53:50 +0200 Subject: [PATCH 2/9] test(cli-&-config-&-utils): basic testing for ci --- tests/cli_tests.rs | 73 ++++++++++++++++++++++++++++ tests/config_tests.rs | 73 ++++++++++++++++++++++++++++ tests/utils_tests.rs | 109 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 tests/cli_tests.rs create mode 100644 tests/config_tests.rs create mode 100644 tests/utils_tests.rs diff --git a/tests/cli_tests.rs b/tests/cli_tests.rs new file mode 100644 index 0000000..795741a --- /dev/null +++ b/tests/cli_tests.rs @@ -0,0 +1,73 @@ +use clap::Parser; +use leetcode_cli::cli::{Cli, Commands}; + +#[test] +fn test_cli_info_command() { + let args = vec!["leetcode_cli", "info", "--id", "1"]; + let cli = Cli::try_parse_from(args).unwrap(); + + match cli.command { + Commands::Info { id } => assert_eq!(id, 1), + _ => panic!("Expected Info command"), + } +} + +#[test] +fn test_cli_start_command() { + let args = vec!["leetcode_cli", "start", "--id", "1", "--lang", "rust"]; + let cli = Cli::try_parse_from(args).unwrap(); + + match cli.command { + Commands::Start { id, language } => { + assert_eq!(id, 1); + assert_eq!(language, "rust"); + }, + _ => panic!("Expected Start command"), + } +} + +#[test] +fn test_cli_test_command() { + let args = vec!["leetcode_cli", "test", "--id", "1", "--file", "main.rs"]; + let cli = Cli::try_parse_from(args).unwrap(); + + match cli.command { + Commands::Test { id, path_to_file } => { + assert_eq!(id, 1); + assert_eq!(path_to_file, "main.rs"); + }, + _ => panic!("Expected Test command"), + } +} + +#[test] +fn test_cli_submit_command() { + let args = vec!["leetcode_cli", "submit", "--id", "1", "--file", "solution.py"]; + let cli = Cli::try_parse_from(args).unwrap(); + + match cli.command { + Commands::Submit { id, path_to_file } => { + assert_eq!(id, 1); + assert_eq!(path_to_file, "solution.py"); + }, + _ => panic!("Expected Submit command"), + } +} + +#[test] +fn test_cli_debug_command() { + let args = vec!["leetcode_cli", "debug"]; + let cli = Cli::try_parse_from(args).unwrap(); + + match cli.command { + Commands::Debug {} => (), + _ => panic!("Expected Debug command"), + } +} + +#[test] +fn test_cli_invalid_command() { + let args = vec!["leetcode_cli", "invalid"]; + let result = Cli::try_parse_from(args); + assert!(result.is_err()); +} \ No newline at end of file diff --git a/tests/config_tests.rs b/tests/config_tests.rs new file mode 100644 index 0000000..cc6be36 --- /dev/null +++ b/tests/config_tests.rs @@ -0,0 +1,73 @@ +use std::path::PathBuf; + +use leetcode_cli::config::Config; +use tempfile::TempDir; + +#[test] +fn test_config_new() { + let config = Config::new(); + + // Check that home_dir is set + assert!(config.leetcode_dir_path.is_some()); + + let expected_path = dirs::home_dir() + .expect("Unable to determine home directory") + .join("leetcode"); + assert_eq!(config.leetcode_dir_path.unwrap(), expected_path); +} + +#[test] +fn test_resolve_leetcode_dir_with_tilde() { + let mut config = Config::new(); + config.leetcode_dir_path = Some(PathBuf::from("~/leetcode")); + + let result = config.resolve_leetcode_dir(); + assert!(result.is_ok()); + + let resolved_path = result.unwrap(); + assert!(resolved_path.is_absolute()); + assert!(resolved_path.ends_with("leetcode")); +} + +#[test] +fn test_resolve_leetcode_dir_absolute_path() { + let temp_dir = TempDir::new().unwrap(); + let mut config = Config::new(); + config.leetcode_dir_path = Some(temp_dir.path().to_path_buf()); + + let result = config.resolve_leetcode_dir(); + assert!(result.is_ok()); + + let resolved_path = result.unwrap(); + assert!(resolved_path.exists()); +} + +#[test] +fn test_resolve_leetcode_dir_no_path() { + let mut config = Config::new(); + config.leetcode_dir_path = None; + + let result = config.resolve_leetcode_dir(); + assert!(result.is_err()); +} + +#[test] +fn test_config_file_creation() { + let home_dir = + dirs::home_dir().expect("Unable to determine home directory"); + let config_file = home_dir.join(".config/leetcode-cli/config.toml"); + let mut config = Config::new(); + match config.status() { + Ok(_) => { + // If the status check passes, the config file should exist + assert!( + config_file.exists(), + "Config file should exist at: {:?}", + config_file + ); + }, + Err(e) => { + panic!("Failed to check config status: {}", e); + }, + } +} diff --git a/tests/utils_tests.rs b/tests/utils_tests.rs new file mode 100644 index 0000000..bb0b335 --- /dev/null +++ b/tests/utils_tests.rs @@ -0,0 +1,109 @@ +use leetcode_cli::{ + config::Config, + utils, +}; +use leetcoderustapi::ProgrammingLanguage; +fn assert_language(actual: ProgrammingLanguage, expected: ProgrammingLanguage) { + let actual_str = format!("{:?}", actual); + let expected_str = format!("{:?}", expected); + assert_eq!( + actual_str, expected_str, + "Languages don't match: got {:?}, expected {:?}", + actual, expected + ); +} +#[test] +fn test_config_creation() { + let config = Config::new(); + assert!(config.leetcode_dir_path.is_some()); +} + +#[test] +fn test_parse_programming_language() { + // Test valid programming languages + assert_language( + utils::parse_programming_language("rust"), + ProgrammingLanguage::Rust, + ); + assert_language( + utils::parse_programming_language("python3"), + ProgrammingLanguage::Python3, + ); + assert_language( + utils::parse_programming_language("javascript"), + ProgrammingLanguage::JavaScript, + ); +} + +#[test] +fn test_extension_programming_language() { + // Test valid extensions to programming languages + assert_language( + utils::extension_programming_language("rs"), + ProgrammingLanguage::Rust, + ); + assert_language( + utils::extension_programming_language("py"), + ProgrammingLanguage::Python3, + ); + assert_language( + utils::extension_programming_language("js"), + ProgrammingLanguage::JavaScript, + ); +} + +#[test] +fn test_get_file_name() { + // Test language to filename mapping + assert_eq!(utils::get_file_name(&ProgrammingLanguage::Rust), "main.rs"); + assert_eq!(utils::get_file_name(&ProgrammingLanguage::Python3), "main.py"); + assert_eq!( + utils::get_file_name(&ProgrammingLanguage::JavaScript), + "main.js" + ); +} + +#[test] +fn test_language_to_string() { + // Test language enum to string conversion + assert_eq!(utils::language_to_string(&ProgrammingLanguage::Rust), "rust"); + assert_eq!( + utils::language_to_string(&ProgrammingLanguage::Python3), + "python3" + ); +} + +#[cfg(test)] +mod file_operations { + use std::fs; + + use tempfile::TempDir; + + use super::*; + + #[test] + fn test_ensure_directory_exists() { + let temp_dir = TempDir::new().unwrap(); + let test_path = temp_dir.path().join("test_dir"); + + let result = utils::ensure_directory_exists(&test_path); + assert!(result.is_ok()); + assert!(test_path.exists()); + } + + #[test] + fn test_write_to_file() { + let temp_dir = TempDir::new().unwrap(); + let content = "Hello, World!"; + let filename = "test.txt"; + + let result = utils::write_to_file(temp_dir.path(), filename, content); + assert!(result.is_ok()); + + let file_path = temp_dir.path().join(filename); + assert!(file_path.exists()); + + let read_content = fs::read_to_string(file_path).unwrap(); + assert_eq!(read_content, content); + } +} From b14ea886430d1270d8ac3edf7f532a22adc185db Mon Sep 17 00:00:00 2001 From: dfayd <78728332+dfayd0@users.noreply.github.com> Date: Thu, 29 May 2025 14:55:17 +0200 Subject: [PATCH 3/9] fix(config): bug and fix discovery thanks to first test draft --- src/config.rs | 107 +++++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 76 deletions(-) diff --git a/src/config.rs b/src/config.rs index 0aaf975..aed2767 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,9 @@ use std::{ fs, - io, + io::{ + self, + Error, + }, path::PathBuf, }; @@ -8,9 +11,9 @@ use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct Config { - home_dir: Option, - config_dir: Option, - config_file: Option, + home_dir: PathBuf, + config_dir: PathBuf, + config_file: PathBuf, pub leetcode_token: Option, pub default_language: Option, pub leetcode_dir_path: Option, @@ -25,33 +28,29 @@ impl Config { let default_leetcode_dir = home_dir.join("leetcode"); Config { - home_dir: Some(home_dir), - config_dir: Some(config_dir), - config_file: Some(config_file), - leetcode_token: None, - default_language: None, + home_dir, + config_dir, + config_file, + leetcode_token: None, + default_language: None, leetcode_dir_path: Some(default_leetcode_dir), } } /// create a config file in ~/.config/leetcode-cli/config.toml fn create_config_file(&self) { - let config_dir = - self.config_dir.as_ref().expect("Config directory not set"); - - std::fs::create_dir_all(&config_dir) + std::fs::create_dir_all(&self.config_dir) .expect("Unable to create directory"); - let config_file = config_dir.join("config.toml"); + let config_file = self.config_dir.join("config.toml"); std::fs::File::create(&config_file).expect("Unable to create file"); } /// check for a config file in ~/.config/leetcode-cli/config.toml - /// read it or create it if it doesn't exist with default values + /// read it or create it with default values if it doesn't exist /// load the config in Config struct and check if the token is valid - /// if the token is not valid, generate a new one - pub async fn status(&mut self) -> Result<(), reqwest::Error> { - if self.config_file.is_some() { - let config_file = - std::fs::read_to_string(self.config_file.as_ref().unwrap()) - .expect("Unable to read file"); + pub fn status(&mut self) -> Result<(), io::Error> { + // check if the config file exists and is readable + if self.config_file.is_file() { + let config_file = std::fs::read_to_string(&self.config_file) + .expect("Unable to read file"); let config: Config = toml::from_str(&config_file).expect("Unable to parse file"); self.leetcode_token = config.leetcode_token; @@ -59,59 +58,23 @@ impl Config { if let Some(custom) = config.leetcode_dir_path { self.leetcode_dir_path = Some(PathBuf::from(custom)); } - self.check_token().await?; + self.check_token()?; } else { self.create_config_file(); - self.generate_token(); } Ok(()) } - async fn test_token_validity(&self) -> Result { - let token = self.leetcode_token.as_ref().unwrap(); - let url = format!( - "https://leetcode.com/api/user/check_token/?token={}", - token - ); - let response = reqwest::get(&url).await.unwrap(); - if response.status().is_success() { - return Ok(true); - } - Ok(false) - } - - async fn check_token(&mut self) -> Result { - let token = self.leetcode_token.as_ref(); - if token.is_none() { - println!("Leetcode token not found"); - println!("Let's set it together."); - self.generate_token(); + fn check_token(&mut self) -> Result { + if self.leetcode_token.is_some() { return Ok(true); } - self.test_token_validity().await - } - - /// Generate a new LeetCode token by prompting the user to visit the - /// LeetCode login page and copy the token - fn generate_token(&mut self) { - unimplemented!( - "generating token is not implemented yet. Refer to the README for \ - instructions." - ); - println!("Generating token..."); - println!("Please visit https://leetcode.com/submissions/#/1"); - println!("and copy the token here:"); - let mut token = String::new(); - std::io::stdin() - .read_line(&mut token) - .expect("Failed to read line"); - let token = token.trim(); - if token.is_empty() { - println!("Token cannot be empty"); - return; - } - self.leetcode_token = Some(token.to_string()); - println!("Token set to: {}", token); + println!("No Leetcode token found."); + Err(Error::new( + io::ErrorKind::NotFound, + "Leetcode token not found. Please set it in the config file \ + following readme instructions.", + )) } /// Resolve the configured LeetCode directory, expand ~, canonicalize, and @@ -127,22 +90,14 @@ impl Config { }; let raw_str = raw.as_ref(); let mut path = if raw_str == "~" { - self.home_dir.clone().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Unable to find home directory", - ))? + self.home_dir.clone() } else if raw_str.starts_with("~/") { - let home = dirs::home_dir().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Unable to find home directory", - ))?; + let home = self.home_dir.clone(); home.join(&raw_str[2..]) } else { PathBuf::from(raw_str) }; - // Canonicalize parent to normalize .. and . components path = fs::canonicalize(&path).unwrap_or(path); - // Ensure directory exists fs::create_dir_all(&path)?; Ok(path) } From 94aae3ca6f372847ff25aad72f49d5f95c1f35f1 Mon Sep 17 00:00:00 2001 From: dfayd <78728332+dfayd0@users.noreply.github.com> Date: Thu, 29 May 2025 15:05:41 +0200 Subject: [PATCH 4/9] fix(rustfmt): Warning: Unknown configuration option `trailling_semicolons` --- .rustfmt.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 45351fb..3618b98 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -6,8 +6,6 @@ color = "Always" format_strings = true space_after_colon = true -trailling_semicolons = false - # structs struct_field_align_threshold = 20 struct_lit_single_line = false From cb368696b3c3d1ad65ef45ef360497cf46938f35 Mon Sep 17 00:00:00 2001 From: dfayd <78728332+dfayd0@users.noreply.github.com> Date: Thu, 29 May 2025 15:06:00 +0200 Subject: [PATCH 5/9] style(cli_tests): fmt --- tests/cli_tests.rs | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/tests/cli_tests.rs b/tests/cli_tests.rs index 795741a..d1aa4f5 100644 --- a/tests/cli_tests.rs +++ b/tests/cli_tests.rs @@ -1,13 +1,18 @@ use clap::Parser; -use leetcode_cli::cli::{Cli, Commands}; +use leetcode_cli::cli::{ + Cli, + Commands, +}; #[test] fn test_cli_info_command() { let args = vec!["leetcode_cli", "info", "--id", "1"]; let cli = Cli::try_parse_from(args).unwrap(); - + match cli.command { - Commands::Info { id } => assert_eq!(id, 1), + Commands::Info { + id, + } => assert_eq!(id, 1), _ => panic!("Expected Info command"), } } @@ -16,9 +21,12 @@ fn test_cli_info_command() { fn test_cli_start_command() { let args = vec!["leetcode_cli", "start", "--id", "1", "--lang", "rust"]; let cli = Cli::try_parse_from(args).unwrap(); - + match cli.command { - Commands::Start { id, language } => { + Commands::Start { + id, + language, + } => { assert_eq!(id, 1); assert_eq!(language, "rust"); }, @@ -30,9 +38,12 @@ fn test_cli_start_command() { fn test_cli_test_command() { let args = vec!["leetcode_cli", "test", "--id", "1", "--file", "main.rs"]; let cli = Cli::try_parse_from(args).unwrap(); - + match cli.command { - Commands::Test { id, path_to_file } => { + Commands::Test { + id, + path_to_file, + } => { assert_eq!(id, 1); assert_eq!(path_to_file, "main.rs"); }, @@ -42,11 +53,21 @@ fn test_cli_test_command() { #[test] fn test_cli_submit_command() { - let args = vec!["leetcode_cli", "submit", "--id", "1", "--file", "solution.py"]; + let args = vec![ + "leetcode_cli", + "submit", + "--id", + "1", + "--file", + "solution.py", + ]; let cli = Cli::try_parse_from(args).unwrap(); - + match cli.command { - Commands::Submit { id, path_to_file } => { + Commands::Submit { + id, + path_to_file, + } => { assert_eq!(id, 1); assert_eq!(path_to_file, "solution.py"); }, @@ -58,7 +79,7 @@ fn test_cli_submit_command() { fn test_cli_debug_command() { let args = vec!["leetcode_cli", "debug"]; let cli = Cli::try_parse_from(args).unwrap(); - + match cli.command { Commands::Debug {} => (), _ => panic!("Expected Debug command"), @@ -70,4 +91,4 @@ fn test_cli_invalid_command() { let args = vec!["leetcode_cli", "invalid"]; let result = Cli::try_parse_from(args); assert!(result.is_err()); -} \ No newline at end of file +} From e120d74b589303f0c38732bafbfbec3c815391ae Mon Sep 17 00:00:00 2001 From: dfayd0 <78728332+dfayd0@users.noreply.github.com> Date: Sun, 1 Jun 2025 13:34:49 +0200 Subject: [PATCH 6/9] refactor(*): the config is getting it's own struct, adapting the rest of the code to that --- src/cli.rs | 1 - src/config.rs | 40 ++++++++++++++++++++------------------ src/leetcode_api_runner.rs | 15 +++++++------- src/lib.rs | 2 +- src/main.rs | 11 ++++------- 5 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index c6d4e86..a2c35f0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -35,5 +35,4 @@ pub enum Commands { #[arg(short = 'p', long = "file")] path_to_file: String, }, - Debug {}, } diff --git a/src/config.rs b/src/config.rs index aed2767..c28c5a9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,16 +10,21 @@ use std::{ use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] -pub struct Config { - home_dir: PathBuf, - config_dir: PathBuf, - config_file: PathBuf, - pub leetcode_token: Option, +pub struct ConfigFile { + pub leetcode_token: String, pub default_language: Option, pub leetcode_dir_path: Option, } -impl Config { +#[derive(Deserialize, Debug, Clone)] +pub struct RuntimeConfigSetup { + pub home_dir: PathBuf, + pub config_dir: PathBuf, + pub config_file: PathBuf, + pub config: ConfigFile, +} + +impl RuntimeConfigSetup { pub fn new() -> Self { let home_dir = dirs::home_dir().expect("Unable to determine home directory"); @@ -27,13 +32,15 @@ impl Config { let config_file = config_dir.join("config.toml"); let default_leetcode_dir = home_dir.join("leetcode"); - Config { + RuntimeConfigSetup { home_dir, config_dir, config_file, - leetcode_token: None, - default_language: None, - leetcode_dir_path: Some(default_leetcode_dir), + config: ConfigFile { + leetcode_token: String::new(), + default_language: None, + leetcode_dir_path: Some(default_leetcode_dir), + }, } } /// create a config file in ~/.config/leetcode-cli/config.toml @@ -51,13 +58,8 @@ impl Config { if self.config_file.is_file() { let config_file = std::fs::read_to_string(&self.config_file) .expect("Unable to read file"); - let config: Config = - toml::from_str(&config_file).expect("Unable to parse file"); - self.leetcode_token = config.leetcode_token; - self.default_language = config.default_language; - if let Some(custom) = config.leetcode_dir_path { - self.leetcode_dir_path = Some(PathBuf::from(custom)); - } + let _: ConfigFile = + toml::from_str(&config_file).expect("Unable to parse config"); self.check_token()?; } else { self.create_config_file(); @@ -66,7 +68,7 @@ impl Config { } fn check_token(&mut self) -> Result { - if self.leetcode_token.is_some() { + if !self.config.leetcode_token.is_empty() { return Ok(true); } println!("No Leetcode token found."); @@ -80,7 +82,7 @@ impl Config { /// Resolve the configured LeetCode directory, expand ~, canonicalize, and /// create if missing. pub fn resolve_leetcode_dir(&self) -> io::Result { - let raw = if let Some(ref custom) = self.leetcode_dir_path { + let raw = if let Some(ref custom) = self.config.leetcode_dir_path { custom.to_string_lossy() } else { return Err(io::Error::new( diff --git a/src/leetcode_api_runner.rs b/src/leetcode_api_runner.rs index 31a25b0..278c394 100644 --- a/src/leetcode_api_runner.rs +++ b/src/leetcode_api_runner.rs @@ -8,7 +8,7 @@ use leetcoderustapi::{ use nanohtml2text::html2text; use crate::{ - config::Config, + config::RuntimeConfigSetup, utils::{ self, ensure_directory_exists, @@ -18,16 +18,15 @@ use crate::{ }; pub struct LeetcodeApiRunner { - config: Config, - api: UserApi, + rcs: RuntimeConfigSetup, + api: UserApi, } impl LeetcodeApiRunner { - pub async fn new(config: &mut Config) -> Self { - let token = config.leetcode_token.take().unwrap(); - let api = UserApi::new(&token).await.unwrap(); + pub async fn new(rcs: RuntimeConfigSetup) -> Self { + let api = UserApi::new(&rcs.config.leetcode_token).await.unwrap(); LeetcodeApiRunner { - config: config.clone(), + rcs, api, } } @@ -72,7 +71,7 @@ impl LeetcodeApiRunner { fn prepare_problem_directory( &self, id: u32, pb_name: &str, language: &ProgrammingLanguage, ) -> io::Result { - let leetcode_dir = self.config.resolve_leetcode_dir()?; + let leetcode_dir = self.rcs.resolve_leetcode_dir()?; let problem_dir = leetcode_dir.join(format!("{}_{}", id, pb_name)); ensure_directory_exists(&problem_dir)?; diff --git a/src/lib.rs b/src/lib.rs index eff6465..497c796 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,5 +7,5 @@ pub use cli::{ Cli, Commands, }; -pub use config::Config; +pub use config::RuntimeConfigSetup; pub use leetcode_api_runner::LeetcodeApiRunner; diff --git a/src/main.rs b/src/main.rs index c332908..c240346 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,16 @@ use leetcode_cli::{ utils, Cli, Commands, - Config, LeetcodeApiRunner, + RuntimeConfigSetup, }; #[tokio::main] async fn main() -> Result<(), Box> { let cli = Cli::parse(); - let mut config = Config::new(); - config.status()?; - let api_runner = LeetcodeApiRunner::new(&mut config).await; + let mut rcs = RuntimeConfigSetup::new(); + rcs.status()?; + let api_runner = LeetcodeApiRunner::new(rcs).await; match &cli.command { Commands::Info { @@ -47,9 +47,6 @@ async fn main() -> Result<(), Box> { .await?; println!("Submit result: {}", submit_result); }, - Commands::Debug {} => { - println!("{:#?}", config.clone()); - }, } Ok(()) } From 5a7750f734e65d863734d13e87bccff215b9aee0 Mon Sep 17 00:00:00 2001 From: dfayd0 <78728332+dfayd0@users.noreply.github.com> Date: Sun, 1 Jun 2025 13:35:40 +0200 Subject: [PATCH 7/9] test(*): modifying tests accordingly to breaking changes --- tests/cli_tests.rs | 11 ----------- tests/config_tests.rs | 30 ++++++++++++++---------------- tests/utils_tests.rs | 11 ++--------- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/tests/cli_tests.rs b/tests/cli_tests.rs index d1aa4f5..bd08c2a 100644 --- a/tests/cli_tests.rs +++ b/tests/cli_tests.rs @@ -75,17 +75,6 @@ fn test_cli_submit_command() { } } -#[test] -fn test_cli_debug_command() { - let args = vec!["leetcode_cli", "debug"]; - let cli = Cli::try_parse_from(args).unwrap(); - - match cli.command { - Commands::Debug {} => (), - _ => panic!("Expected Debug command"), - } -} - #[test] fn test_cli_invalid_command() { let args = vec!["leetcode_cli", "invalid"]; diff --git a/tests/config_tests.rs b/tests/config_tests.rs index cc6be36..82e2bd2 100644 --- a/tests/config_tests.rs +++ b/tests/config_tests.rs @@ -1,27 +1,26 @@ use std::path::PathBuf; -use leetcode_cli::config::Config; +use leetcode_cli::config::RuntimeConfigSetup; use tempfile::TempDir; #[test] fn test_config_new() { - let config = Config::new(); + let rcs = RuntimeConfigSetup::new(); - // Check that home_dir is set - assert!(config.leetcode_dir_path.is_some()); + assert!(rcs.home_dir.exists()); let expected_path = dirs::home_dir() .expect("Unable to determine home directory") .join("leetcode"); - assert_eq!(config.leetcode_dir_path.unwrap(), expected_path); + assert_eq!(rcs.config.leetcode_dir_path.unwrap(), expected_path); } #[test] fn test_resolve_leetcode_dir_with_tilde() { - let mut config = Config::new(); - config.leetcode_dir_path = Some(PathBuf::from("~/leetcode")); + let mut rcs = RuntimeConfigSetup::new(); + rcs.config.leetcode_dir_path = Some(PathBuf::from("~/leetcode")); - let result = config.resolve_leetcode_dir(); + let result = rcs.resolve_leetcode_dir(); assert!(result.is_ok()); let resolved_path = result.unwrap(); @@ -32,10 +31,10 @@ fn test_resolve_leetcode_dir_with_tilde() { #[test] fn test_resolve_leetcode_dir_absolute_path() { let temp_dir = TempDir::new().unwrap(); - let mut config = Config::new(); - config.leetcode_dir_path = Some(temp_dir.path().to_path_buf()); + let mut rcs = RuntimeConfigSetup::new(); + rcs.config.leetcode_dir_path = Some(temp_dir.path().to_path_buf()); - let result = config.resolve_leetcode_dir(); + let result = rcs.resolve_leetcode_dir(); assert!(result.is_ok()); let resolved_path = result.unwrap(); @@ -44,10 +43,10 @@ fn test_resolve_leetcode_dir_absolute_path() { #[test] fn test_resolve_leetcode_dir_no_path() { - let mut config = Config::new(); - config.leetcode_dir_path = None; + let mut rcs = RuntimeConfigSetup::new(); + rcs.config.leetcode_dir_path = None; - let result = config.resolve_leetcode_dir(); + let result = rcs.resolve_leetcode_dir(); assert!(result.is_err()); } @@ -56,10 +55,9 @@ fn test_config_file_creation() { let home_dir = dirs::home_dir().expect("Unable to determine home directory"); let config_file = home_dir.join(".config/leetcode-cli/config.toml"); - let mut config = Config::new(); + let mut config = RuntimeConfigSetup::new(); match config.status() { Ok(_) => { - // If the status check passes, the config file should exist assert!( config_file.exists(), "Config file should exist at: {:?}", diff --git a/tests/utils_tests.rs b/tests/utils_tests.rs index bb0b335..2b41335 100644 --- a/tests/utils_tests.rs +++ b/tests/utils_tests.rs @@ -1,8 +1,6 @@ -use leetcode_cli::{ - config::Config, - utils, -}; +use leetcode_cli::utils; use leetcoderustapi::ProgrammingLanguage; + fn assert_language(actual: ProgrammingLanguage, expected: ProgrammingLanguage) { let actual_str = format!("{:?}", actual); let expected_str = format!("{:?}", expected); @@ -12,11 +10,6 @@ fn assert_language(actual: ProgrammingLanguage, expected: ProgrammingLanguage) { actual, expected ); } -#[test] -fn test_config_creation() { - let config = Config::new(); - assert!(config.leetcode_dir_path.is_some()); -} #[test] fn test_parse_programming_language() { From 5f3b3f1c20978388c50720ab15fce263f6687b47 Mon Sep 17 00:00:00 2001 From: dfayd0 <78728332+dfayd0@users.noreply.github.com> Date: Sun, 1 Jun 2025 14:30:21 +0200 Subject: [PATCH 8/9] fix(config): addiing config_file values to the rcs config_file --- src/config.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index c28c5a9..df8d1b5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -54,12 +54,20 @@ impl RuntimeConfigSetup { /// read it or create it with default values if it doesn't exist /// load the config in Config struct and check if the token is valid pub fn status(&mut self) -> Result<(), io::Error> { - // check if the config file exists and is readable if self.config_file.is_file() { let config_file = std::fs::read_to_string(&self.config_file) .expect("Unable to read file"); - let _: ConfigFile = + let parsed_config: ConfigFile = toml::from_str(&config_file).expect("Unable to parse config"); + if !parsed_config.leetcode_token.is_empty() { + self.config.leetcode_token = parsed_config.leetcode_token; + } + if parsed_config.default_language.is_some() { + self.config.default_language = parsed_config.default_language; + } + if parsed_config.leetcode_dir_path.is_some() { + self.config.leetcode_dir_path = parsed_config.leetcode_dir_path; + } self.check_token()?; } else { self.create_config_file(); From 1e8197422e38cdc47dae469188a374a3d8c0e8bb Mon Sep 17 00:00:00 2001 From: dfayd0 <78728332+dfayd0@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:19:02 +0200 Subject: [PATCH 9/9] refactor(config): removing useless legacy fn --- src/config.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index df8d1b5..c7a3cc7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,6 @@ use std::{ fs, io::{ self, - Error, }, path::PathBuf, }; @@ -68,25 +67,12 @@ impl RuntimeConfigSetup { if parsed_config.leetcode_dir_path.is_some() { self.config.leetcode_dir_path = parsed_config.leetcode_dir_path; } - self.check_token()?; } else { self.create_config_file(); } Ok(()) } - fn check_token(&mut self) -> Result { - if !self.config.leetcode_token.is_empty() { - return Ok(true); - } - println!("No Leetcode token found."); - Err(Error::new( - io::ErrorKind::NotFound, - "Leetcode token not found. Please set it in the config file \ - following readme instructions.", - )) - } - /// Resolve the configured LeetCode directory, expand ~, canonicalize, and /// create if missing. pub fn resolve_leetcode_dir(&self) -> io::Result {