Skip to content

Commit 3ecbbfd

Browse files
committed
Improve error handling in main.rs
And in others as a side effect
1 parent 5fd3c09 commit 3ecbbfd

File tree

4 files changed

+105
-171
lines changed

4 files changed

+105
-171
lines changed

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/question.rs

Lines changed: 44 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
use crate::handlers::leetcode::*;
22

3+
use eyre::{bail, Context, Result};
4+
35
impl LeetCode<Authorized> {
4-
pub fn get_daily_challenge(&self) -> Result<DailyChallenge, &str> {
6+
pub fn get_daily_challenge(&self) -> Result<DailyChallenge> {
57
let url = "https://leetcode.com/graphql";
68
let client = &self.client;
79
let query = GraphqlRequest {
810
query: "\n query questionOfToday {\n activeDailyCodingChallengeQuestion {\n date\n userStatus\n link\n question {\n acRate\n difficulty\n freqBar\n frontendQuestionId: questionFrontendId\n isFavor\n paidOnly: isPaidOnly\n status\n title\n titleSlug\n hasVideoSolution\n hasSolution\n topicTags {\n name\n id\n slug\n }\n }\n }\n}\n ".to_string(),
911
variables: "{}".to_string(),
1012
};
11-
let Ok(data) = client.post(url).json(&query).send() else {
12-
return Err("Failed to fetch daily challenge from leetcode!");
13-
};
13+
14+
let data = client.post(url).json(&query).send()?;
15+
1416
// println!("{:?}", data.text());
1517
// todo!();
18+
1619
#[derive(Deserialize)]
1720
#[allow(non_snake_case)]
1821
struct DailyChallengeWrapper {
@@ -22,43 +25,38 @@ impl LeetCode<Authorized> {
2225
struct Wrapper {
2326
data: DailyChallengeWrapper,
2427
}
28+
2529
Ok(data
26-
.json::<Wrapper>()
27-
.map_err(|_| "Failed to parse daily challenge!")?
30+
.json::<Wrapper>()?
2831
.data
2932
.activeDailyCodingChallengeQuestion)
3033
}
3134

32-
pub fn get_metadata(&self) -> Result<UserMetadata, &str> {
35+
pub fn get_metadata(&self) -> Result<UserMetadata> {
3336
let client = &self.client;
34-
let Ok(data) = client
37+
let data = client
3538
.get("https://leetcode.com/api/problems/all/")
36-
.send() else {
37-
return Err("Failed to fetch metadata from leetcode!");
38-
};
39+
.send()
40+
.wrap_err("Failed to fetch metadata from LeetCode")?;
3941

4042
let metadata = data
4143
.json::<UserMetadata>()
42-
.map_err(|_| "Failed to parse metadata! Try renewing cookie");
43-
if let Ok(metadata) = metadata.as_ref() {
44-
if metadata.user_name == "" {
45-
return Err("Cookie invalid. Renew cookies");
46-
}
44+
.wrap_err("Failed to parse metadata, Try renewing cookie")?;
45+
if metadata.user_name.is_empty() {
46+
bail!("Cookie invalid. Renew cookies");
4747
}
48-
metadata
48+
Ok(metadata)
4949
}
5050

51-
pub fn question_content(&self, title_slug: &str) -> Result<LeetcodeQuestion, &str> {
51+
pub fn question_content(&self, title_slug: &str) -> Result<LeetcodeQuestion> {
5252
let client = &self.client;
5353
let url = "https://leetcode.com/graphql";
5454
let query = GraphqlRequest {
5555
query: "query questionContent($titleSlug: String!) { question(titleSlug: $titleSlug) { content mysqlSchemas }}".to_string(),
5656
variables: serde_json::to_string(&Variables { titleSlug: title_slug.to_string() }).unwrap(),
5757
};
5858

59-
let Ok(data) = client.post(url).json(&query).send() else {
60-
return Err("Failed to fetch question id from leetcode!");
61-
};
59+
let data = client.post(url).json(&query).send()?;
6260

6361
#[derive(Deserialize)]
6462
struct QuestionWrapper {
@@ -72,18 +70,14 @@ impl LeetCode<Authorized> {
7270
let query = "\n query questionEditorData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n codeSnippets {\n lang\n langSlug\n code\n }\n envInfo\n enableRunCode\n }\n}\n ";
7371
let varibales = serde_json::to_string(&Variables {
7472
titleSlug: title_slug.to_string(),
75-
})
76-
.unwrap();
77-
let Ok(boiler_code) = client
73+
})?;
74+
let boiler_code = client
7875
.post(url)
7976
.json(&GraphqlRequest {
8077
query: query.to_string(),
8178
variables: varibales,
8279
})
83-
.send()
84-
else {
85-
return Err("Failed to fetch boiler plate code!");
86-
};
80+
.send()?;
8781

8882
#[derive(Debug, Deserialize)]
8983
#[allow(non_snake_case)]
@@ -99,51 +93,37 @@ impl LeetCode<Authorized> {
9993
data: WrapperData,
10094
}
10195

102-
let boiler_code_vector = boiler_code
103-
.json::<Wrapper>()
104-
.map_err(|_| "Failed to parse boiler plate code!")?
105-
.data
106-
.question
107-
.codeSnippets;
96+
let boiler_code_vector = boiler_code.json::<Wrapper>()?.data.question.codeSnippets;
10897

109-
let boiler_code_vector = boiler_code_vector
98+
let mut boiler_code_vector = boiler_code_vector
11099
.into_iter()
111100
.filter(|code| code.is_supported())
112101
.collect::<Vec<_>>();
113102

114103
// ask user to specify language among these options without using external lib
115-
let boiler_code = if boiler_code_vector.len() == 1 {
116-
boiler_code_vector.into_iter().next().unwrap()
117-
} else if !boiler_code_vector.is_empty() {
118-
let mut input = String::new();
119-
println!("\nPlease select a language from the following options :");
120-
for (i, code) in boiler_code_vector.iter().enumerate() {
121-
println!("{}: {}", i, code.langSlug);
122-
}
123-
println!(
124-
"\nFor example : Input \"{}\" for {}",
125-
0, &boiler_code_vector[0].langSlug
126-
);
127-
if let Err(_) = std::io::stdin().read_line(&mut input) {
128-
return Err("Failed to read input!");
104+
let boiler_code = match boiler_code_vector.len() {
105+
0 => bail!("No boiler plate code available in supported language!"),
106+
1 => boiler_code_vector.swap_remove(0),
107+
_ => {
108+
let mut input = String::new();
109+
println!("\nPlease select a language from the following options :");
110+
for (i, code) in boiler_code_vector.iter().enumerate() {
111+
println!("{}: {}", i, code.langSlug);
112+
}
113+
println!(
114+
"\nFor example : Input \"{}\" for {}",
115+
0, &boiler_code_vector[0].langSlug
116+
);
117+
std::io::stdin().read_line(&mut input)?;
118+
let input = input.trim();
119+
let input = input.parse::<usize>()?;
120+
boiler_code_vector.swap_remove(input)
129121
}
130-
let input = input.trim();
131-
let Ok(input) = input.parse::<usize>() else {
132-
return Err("Invalid input!");
133-
};
134-
if let Some(code) = boiler_code_vector.into_iter().nth(input) {
135-
code
136-
} else {
137-
return Err("Invalid input!");
138-
}
139-
} else {
140-
return Err("No boiler plate code available in supported language!");
141122
};
123+
142124
let mut input = String::new();
143125
println!("Filename (main.{}) : ", &(boiler_code.extension()));
144-
if let Err(_) = std::io::stdin().read_line(&mut input) {
145-
return Err("Failed to read input!");
146-
}
126+
std::io::stdin().read_line(&mut input)?;
147127
let input = input.trim();
148128
let filename = if input.is_empty() {
149129
format!("main.{}", boiler_code.extension())
@@ -152,9 +132,7 @@ impl LeetCode<Authorized> {
152132
};
153133
boiler_code.save_code(&filename, &title_slug);
154134

155-
data.json::<Data>()
156-
.map_err(|_| "Failed to parse question content!")
157-
.map(|op| op.data.question)
135+
Ok(data.json::<Data>().map(|op| op.data.question)?)
158136
}
159137

160138
pub fn question_metadata(&self, title_slug: &str) -> Result<Question, &str> {

0 commit comments

Comments
 (0)