Skip to content

Commit d27a252

Browse files
Merge pull request #9 from coding-kelps/first-tests
First (cli, config, utils) tests
2 parents ee45bc6 + 1e81974 commit d27a252

File tree

10 files changed

+334
-119
lines changed

10 files changed

+334
-119
lines changed

.rustfmt.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ color = "Always"
66
format_strings = true
77
space_after_colon = true
88

9-
trailling_semicolons = false
10-
119
# structs
1210
struct_field_align_threshold = 20
1311
struct_lit_single_line = false

Cargo.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
name = "leetcode-cli"
33
version = "0.1.0"
44
authors = ["Coding Kelps"]
5-
autotests = false
65
categories = ["command-line-utilities"]
76
description = "Interact with LeetCode in your development environment."
87
edition = "2021"
@@ -14,16 +13,25 @@ readme = "README.md"
1413
repository = "https://github.com/coding-kelps/leetcode-cli"
1514
rust-version = "1.83"
1615

16+
[[bin]]
17+
name = "leetcode_cli"
18+
path = "src/main.rs"
19+
20+
[lib]
21+
name = "leetcode_cli"
22+
path = "src/lib.rs"
23+
1724
[dependencies]
1825
leetcoderustapi = "1.0.8"
1926
clap = { version = "4.5.34", features = ["derive"] }
2027
toml = "0.8.20"
2128
toml_edit = "0.22.24"
2229
serde = { version = "1.0.219", features = ["serde_derive"] }
23-
reqwest = "0.12.15"
30+
reqwest = "0.12.18"
2431
tokio = { version = "1.44.1", features = ["full"] }
2532
dirs = "6.0.0"
2633
warp = "0.3.7"
2734
colored = "3.0.0"
2835
nanohtml2text = "0.2.1"
2936
html2md = "0.2.15"
37+
tempfile = "3.20.0"

src/cli.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,4 @@ pub enum Commands {
3535
#[arg(short = 'p', long = "file")]
3636
path_to_file: String,
3737
},
38-
Debug {},
3938
}

src/config.rs

Lines changed: 43 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,82 @@
11
use std::{
22
fs,
3-
io,
3+
io::{
4+
self,
5+
},
46
path::PathBuf,
57
};
68

79
use 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
}

src/leetcode_api_runner.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use leetcoderustapi::{
88
use nanohtml2text::html2text;
99

1010
use crate::{
11-
config::Config,
11+
config::RuntimeConfigSetup,
1212
utils::{
1313
self,
1414
ensure_directory_exists,
@@ -18,16 +18,15 @@ use crate::{
1818
};
1919

2020
pub struct LeetcodeApiRunner {
21-
config: Config,
22-
api: UserApi,
21+
rcs: RuntimeConfigSetup,
22+
api: UserApi,
2323
}
2424

2525
impl LeetcodeApiRunner {
26-
pub async fn new(config: &mut Config) -> Self {
27-
let token = config.leetcode_token.take().unwrap();
28-
let api = UserApi::new(&token).await.unwrap();
26+
pub async fn new(rcs: RuntimeConfigSetup) -> Self {
27+
let api = UserApi::new(&rcs.config.leetcode_token).await.unwrap();
2928
LeetcodeApiRunner {
30-
config: config.clone(),
29+
rcs,
3130
api,
3231
}
3332
}
@@ -72,7 +71,7 @@ impl LeetcodeApiRunner {
7271
fn prepare_problem_directory(
7372
&self, id: u32, pb_name: &str, language: &ProgrammingLanguage,
7473
) -> io::Result<std::path::PathBuf> {
75-
let leetcode_dir = self.config.resolve_leetcode_dir()?;
74+
let leetcode_dir = self.rcs.resolve_leetcode_dir()?;
7675
let problem_dir = leetcode_dir.join(format!("{}_{}", id, pb_name));
7776
ensure_directory_exists(&problem_dir)?;
7877

src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pub mod cli;
2+
pub mod config;
3+
pub mod leetcode_api_runner;
4+
pub mod utils;
5+
6+
pub use cli::{
7+
Cli,
8+
Commands,
9+
};
10+
pub use config::RuntimeConfigSetup;
11+
pub use leetcode_api_runner::LeetcodeApiRunner;

src/main.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
1-
mod cli;
2-
mod config;
3-
mod leetcode_api_runner;
4-
mod utils;
5-
61
use clap::Parser;
7-
use cli::{
2+
use leetcode_cli::{
3+
utils,
84
Cli,
95
Commands,
6+
LeetcodeApiRunner,
7+
RuntimeConfigSetup,
108
};
11-
use config::Config;
12-
use leetcode_api_runner::LeetcodeApiRunner;
139

1410
#[tokio::main]
1511
async fn main() -> Result<(), Box<dyn std::error::Error>> {
1612
let cli = Cli::parse();
17-
let mut config = Config::new();
18-
config.status().await?;
19-
let api_runner = LeetcodeApiRunner::new(&mut config).await;
13+
let mut rcs = RuntimeConfigSetup::new();
14+
rcs.status()?;
15+
let api_runner = LeetcodeApiRunner::new(rcs).await;
2016

2117
match &cli.command {
2218
Commands::Info {
@@ -51,9 +47,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
5147
.await?;
5248
println!("Submit result: {}", submit_result);
5349
},
54-
Commands::Debug {} => {
55-
println!("{:#?}", config.clone());
56-
},
5750
}
5851
Ok(())
5952
}

0 commit comments

Comments
 (0)