Skip to content

Commit a98b2bc

Browse files
authored
Merge pull request #11 from nozwock/error-handling
Improve error handling
2 parents 32fd0a6 + 1fcc654 commit a98b2bc

File tree

8 files changed

+303
-443
lines changed

8 files changed

+303
-443
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[dependencies]
77
clap = { version = "4.2.1", features = ["derive"] }
88
colored = "2.0.0"
9-
reqwest ={version = "0.11.16", features=["blocking", "json"]}
10-
serde = {version="1.0.159", features=["derive"]}
9+
eyre = "0.6.8"
10+
reqwest = { version = "0.11.16", features = ["blocking", "json"] }
11+
serde = { version = "1.0.159", features = ["derive"] }
1112
serde_json = "1.0.95"

src/file_parser/codefile.rs

Lines changed: 51 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use eyre::Result;
2+
13
use super::language::*;
24
use std::path::PathBuf;
35

@@ -20,43 +22,42 @@ impl Default for CodeFile {
2022
}
2123

2224
impl CodeFile {
23-
pub fn from_dir() -> Self {
25+
pub fn from_file(path: &str) -> Result<Self> {
26+
let path = PathBuf::from(&path);
27+
let (_file_name, mut code_file) =
28+
Self::is_valid_file(&path).ok_or_else(|| eyre::eyre!("Invalid file"))?;
29+
let code = std::fs::read_to_string(&path)?;
30+
31+
let (question_title, parsed_code) = Self::parse_code(&code, code_file.language)?;
32+
33+
code_file.question_title = question_title;
34+
code_file.code = parsed_code;
35+
36+
Ok(code_file)
37+
}
38+
39+
pub fn from_dir() -> Result<Self> {
2440
let mut code_file: Option<CodeFile> = None;
25-
let Ok(files) = std::fs::read_dir("./") else {
26-
eprintln!("Error reading the current directory!");
27-
std::process::exit(1);
28-
};
29-
for file in files {
30-
let Ok(file) = file else {
31-
// Bad path
32-
continue
33-
};
41+
for file in std::fs::read_dir(".")?.filter_map(|f| f.ok()) {
3442
let path = file.path();
35-
let Some(valid_file) = Self::is_valid_file(&path) else {continue};
36-
let file_name = valid_file.0;
37-
code_file = Some(valid_file.1);
38-
39-
if file_name.starts_with("main") {
40-
break;
43+
if let Some((file_name, code_file_)) = Self::is_valid_file(&path) {
44+
code_file = Some(code_file_);
45+
if file_name.starts_with("main") {
46+
break;
47+
}
4148
}
4249
}
43-
let mut code_file = code_file.unwrap_or_else(|| {
44-
eprintln!("No code file found! Try creating a file named with proper extension",);
45-
std::process::exit(1);
46-
});
47-
let Ok(code) = std::fs::read_to_string(&code_file.path) else {
48-
eprintln!("Error reading the code file!");
49-
std::process::exit(1);
50-
};
51-
52-
let parsed_file = Self::parse_code(&code, code_file.language);
53-
let Ok((question_title, parsed_code)) = parsed_file else{
54-
eprintln!("Error parsing the code file!\n{}", parsed_file.unwrap_err());
55-
std::process::exit(1);
56-
};
50+
let mut code_file = code_file.ok_or_else(|| {
51+
eyre::eyre!("No code file found! Try creating a file named with proper extension")
52+
})?;
53+
let code = std::fs::read_to_string(&code_file.path)?;
54+
55+
let (question_title, parsed_code) = Self::parse_code(&code, code_file.language)?;
56+
5757
code_file.question_title = question_title;
5858
code_file.code = parsed_code;
59-
code_file
59+
60+
Ok(code_file)
6061
}
6162

6263
fn is_valid_file<'a>(path: &'a std::path::PathBuf) -> Option<(&'a str, Self)> {
@@ -75,9 +76,7 @@ impl CodeFile {
7576
))
7677
}
7778

78-
fn parse_code(code: &str, language: Language) -> Result<(String, String), &str> {
79-
let question_title: String;
80-
let parsed_code: String;
79+
fn parse_code(code: &str, language: Language) -> Result<(String, String)> {
8180
let start = code
8281
.find("#LCSTART")
8382
.map(|idx| idx + code[idx..].find('\n').unwrap_or(0))
@@ -87,40 +86,26 @@ impl CodeFile {
8786
.unwrap_or(0);
8887

8988
let end = code.find("#LCEND").unwrap_or(code.len());
90-
if let Some(problem) = code.find("leetcode.com/problems/") {
91-
let problem = (&code[problem..]).split_whitespace().next().unwrap();
92-
let problem = problem.split('/').skip(2).next().unwrap();
93-
question_title = problem.to_string();
94-
} else {
95-
return Err("No leetcode problem found in the code file. Please add the problem link in the code file using comments.");
96-
}
97-
let code = code[start..end].trim();
98-
let code = code
89+
let question_title = code[code.find("leetcode.com/problems/").ok_or_else(|| {
90+
eyre::eyre!(
91+
"No leetcode problem found in the code file. \
92+
Please add the problem link in the code file using comments."
93+
)
94+
})?..]
95+
.split_whitespace()
96+
.next()
97+
.expect("Should be Some since the find method succeed")
98+
.split('/')
99+
.skip(2)
100+
.next()
101+
.ok_or_else(|| eyre::eyre!("Invalid link, expected question identifier"))?
102+
.to_string();
103+
let parsed_code = code[start..end]
104+
.trim()
99105
.trim_end_matches(language.inline_comment_start())
100-
.trim_end();
101-
parsed_code = code.to_string();
106+
.trim_end()
107+
.to_string();
102108

103109
Ok((question_title, parsed_code))
104110
}
105-
106-
pub fn from_file(path: &str) -> Self {
107-
let path = PathBuf::from(&path);
108-
let Some(file) = Self::is_valid_file(&path) else {
109-
eprintln!("Invalid file!");
110-
std::process::exit(1);
111-
};
112-
let (_, mut valid_file) = file;
113-
let Ok(code) = std::fs::read_to_string(&path) else {
114-
eprintln!("Error while reading file {}!", path.display());
115-
std::process::exit(1);
116-
};
117-
let parsed_file = Self::parse_code(&code, valid_file.language);
118-
let Ok((question_title, parsed_code)) = parsed_file else{
119-
eprintln!("Error parsing the code file!\n{}", parsed_file.unwrap_err());
120-
std::process::exit(1);
121-
};
122-
valid_file.question_title = question_title;
123-
valid_file.code = parsed_code;
124-
valid_file
125-
}
126111
}

src/handlers/leetcode.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use super::user::*;
33
use super::utils::*;
44
use crate::file_parser::codefile::CodeFile;
55

6+
use eyre::Result;
67
use serde::Deserialize;
78

89
mod api;
@@ -27,36 +28,34 @@ impl LeetCode {
2728
impl LeetCode<Unauthorized> {
2829
/// # Authenticate with cookie
2930
/// Builds a new reqwest client with the cookie
30-
pub fn authenticate(&self, cookie: &str) -> Result<LeetCode<Authorized>, &str> {
31+
pub fn authenticate(&self, cookie: &str) -> Result<LeetCode<Authorized>> {
3132
let mut headers = reqwest::header::HeaderMap::with_capacity(5);
32-
let Some(csrf_token) = cookie
33+
let csrf_token = cookie
3334
.split(';')
3435
.find(|s| s.contains("csrftoken"))
35-
else {
36-
return Err("No csrf token found");
37-
};
38-
let Some(csrf_token) = csrf_token.split('=').last() else{return Err("No csrf token found"); };
39-
let csrf_token = csrf_token.to_string();
36+
.map(|s| s.split('=').last())
37+
.flatten()
38+
.ok_or_else(|| eyre::eyre!("No csrf token found"))?;
39+
4040
headers.insert(
4141
reqwest::header::COOKIE,
42-
reqwest::header::HeaderValue::from_str(&cookie).unwrap(),
42+
reqwest::header::HeaderValue::from_str(&cookie)?,
4343
);
4444
headers.insert(
4545
reqwest::header::USER_AGENT,
46-
reqwest::header::HeaderValue::from_str("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36").unwrap(),
46+
reqwest::header::HeaderValue::from_str("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36")?,
4747
);
4848
headers.insert(
4949
reqwest::header::REFERER,
50-
reqwest::header::HeaderValue::from_str("https://leetcode.com/").unwrap(),
50+
reqwest::header::HeaderValue::from_str("https://leetcode.com/")?,
5151
);
5252
headers.insert(
5353
reqwest::header::HeaderName::from_static("x-csrftoken"),
54-
reqwest::header::HeaderValue::from_str(csrf_token.as_str()).unwrap(),
54+
reqwest::header::HeaderValue::from_str(csrf_token)?,
5555
);
5656
let client = reqwest::blocking::Client::builder()
5757
.default_headers(headers.clone())
58-
.build()
59-
.unwrap();
58+
.build()?;
6059
Ok(LeetCode {
6160
state: std::marker::PhantomData::<Authorized>,
6261
client,

src/handlers/leetcode/api/execute.rs

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
use crate::handlers::{leetcode::*, utils::ExecutionResult};
22

3+
use eyre::{bail, Context, Result};
4+
35
impl LeetCode<Authorized> {
4-
pub fn execute_default(&self, codefile: &CodeFile) -> Result<ExecutionResult, &str> {
6+
pub fn execute_default(&self, codefile: &CodeFile) -> Result<ExecutionResult> {
57
self.execute(codefile, String::new())
68
}
7-
pub fn execute(
8-
&self,
9-
codefile: &CodeFile,
10-
mut data_input: String,
11-
) -> Result<ExecutionResult, &str> {
9+
pub fn execute(&self, codefile: &CodeFile, mut data_input: String) -> Result<ExecutionResult> {
1210
let question_title = codefile.question_title.clone();
1311
let ques = self.question_metadata(&question_title)?;
14-
if data_input == "" {
12+
if data_input.is_empty() {
1513
data_input = ques.exampleTestcaseList.join("\n");
14+
1615
// write this to testcase.txt
17-
if let Ok(mut file) = std::fs::File::create("testcase.txt") {
18-
if let Ok(_) = std::io::Write::write_all(&mut file, data_input.as_bytes()) {
19-
println!("Wrote default testcases to testcase.txt");
20-
} else {
21-
eprintln!("Failed to write default testcases to testcase.txt!");
22-
}
23-
} else {
24-
eprintln!("Failed to create testcase.txt!");
25-
}
16+
let mut file = std::fs::File::create("testcase.txt")?;
17+
std::io::Write::write_all(&mut file, data_input.as_bytes())?;
18+
println!("Wrote default testcases to testcase.txt");
2619
}
20+
2721
let question_id = ques.questionId;
22+
2823
self._execute(
2924
codefile.language.to_string(),
3025
question_id,
@@ -41,39 +36,45 @@ impl LeetCode<Authorized> {
4136
question_title: String,
4237
typed_code: String,
4338
data_input: String,
44-
) -> Result<ExecutionResult, &str> {
39+
) -> Result<ExecutionResult> {
4540
let client = &self.client;
4641
let url = format!(
4742
"https://leetcode.com/problems/{}/interpret_solution/",
4843
question_title
4944
);
45+
5046
let testcase = TestCaseExec {
5147
lang,
5248
question_id,
5349
question_title,
5450
typed_code,
5551
data_input,
5652
};
57-
let Ok(data)= client.post(&url).json(&testcase).send() else {
58-
return Err("Failed to parse arguments!");
59-
};
60-
let Ok(data) = data.json::<InterpretID>() else{
61-
return Err("Failed to parse JSON from leetcode! Try again after sometime or renew cookie");
62-
};
53+
54+
let data = client
55+
.post(&url)
56+
.json(&testcase)
57+
.send()?
58+
.json::<InterpretID>()
59+
.wrap_err(
60+
"Failed to parse JSON from LeetCode, Try again after sometime or renew your cookie",
61+
)?;
6362

6463
let interpret_id = data.interpret_id;
64+
6565
println!("Executing testcases...");
6666
let mut last_state = PendingState::Unknown;
6767
loop {
6868
let url = format!("https://leetcode.com/submissions/detail/{interpret_id}/check/");
6969
// std::thread::sleep(std::time::Duration::from_secs(7));
70-
let Ok(data) = client.get(&url).send() else {
71-
return Err("Failed to parse arguments!");
72-
};
70+
let data = client
71+
.get(&url)
72+
.send()?
73+
.json::<ExecutionResult>()
74+
.wrap_err(
75+
"Failed to parse JSON from LeetCode, Try again after sometime or renew your cookie",
76+
)?;
7377

74-
let Ok(data) = data.json::<ExecutionResult>() else {
75-
return Err("Failed to parse JSON from leetcode! Try again after sometime or renew cookie");
76-
};
7778
match data {
7879
ExecutionResult::PendingResult(data) => {
7980
let curr_state = data.state();
@@ -89,22 +90,18 @@ impl LeetCode<Authorized> {
8990
}
9091
}
9192
PendingState::Success => {
92-
println!("Your code was executed successfully but we failed to parse result\nCheck on leetcode manually");
93-
std::process::exit(1);
93+
bail!("our code was executed successfully but we failed to parse result\nCheck on leetcode manually");
9494
}
9595
PendingState::Unknown => {
96-
println!(
96+
bail!(
9797
"Status : {}\nKindly report this state to developer",
9898
data.state.as_str()
9999
);
100-
std::process::exit(1);
101100
}
102101
};
103102
last_state = curr_state;
104-
105-
continue;
106103
}
107-
data => return Ok(data),
104+
not_pending_data => return Ok(not_pending_data),
108105
};
109106
}
110107
}

0 commit comments

Comments
 (0)