Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5566870

Browse files
Feat: like vsc-leetcode-cli use @lc code=start and @lc code=end to pick the code (clearloop#77)
* fix: clippy warning for rust 1.61 stable * feat: upgrade dep version, and use Rust 2021 Edition * feat: remove test file * fix: replace deprecated is_present * feat: use scraper to parser html fragment * feat: like vsc-leetcode-cli edit file has extra problem desc 1. edit add file include extra information 2. remove test file, and use memory all_cases 3. use scraper crate parser the desc html fragment * fix: solve issue 72 * not use deprecated code * revert: use generate the test file * feat: storage config path use the absolutely dir, not in the root config * fix: fmt fix
1 parent 1d67017 commit 5566870

File tree

15 files changed

+211
-255
lines changed

15 files changed

+211
-255
lines changed

‎Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ clap = { version = "3.2.8", features = ["cargo"] }
2222
colored = "2.0.0"
2323
dirs = "4.0.0"
2424
env_logger = "0.9.0"
25-
escaper = "0.1.1"
2625
keyring = "1.1.2"
2726
log = "0.4.17"
2827
openssl = "0.10.40"
@@ -32,6 +31,7 @@ serde = { version = "1.0.138", features = ["derive"] }
3231
serde_json = "1.0.82"
3332
toml = "0.5.9"
3433
regex = "1.5.6"
34+
scraper = "0.13.0"
3535

3636
[dependencies.diesel]
3737
version = "1.4.8"

‎src/cache/mod.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ mod sql;
66
use self::models::*;
77
use self::schemas::{problems::dsl::*, tags::dsl::*};
88
use self::sql::*;
9+
use crate::cmds::{CODE_END, CODE_START};
10+
use crate::helper::test_cases_path;
911
use crate::{cfg, err::Error, plugins::LeetCode};
1012
use colored::Colorize;
1113
use diesel::prelude::*;
@@ -26,7 +28,7 @@ pub enum Run {
2628
Submit,
2729
}
2830

29-
impl std::default::Default for Run {
31+
impl Default for Run {
3032
fn default() -> Self {
3133
Run::Submit
3234
}
@@ -37,7 +39,7 @@ impl std::default::Default for Run {
3739
pub struct Cache(pub LeetCode);
3840

3941
impl Cache {
40-
/// Ref to sqliteconnection
42+
/// Ref to sqlite connection
4143
fn conn(&self) -> Result<SqliteConnection, Error> {
4244
Ok(conn(self.0.conf.storage.cache()?))
4345
}
@@ -236,10 +238,10 @@ impl Cache {
236238
&self,
237239
run: Run,
238240
rfid: i32,
239-
testcase: Option<String>,
241+
test_case: Option<String>,
240242
) -> Result<(HashMap<&'static str, String>, [String; 2]), Error> {
241243
trace!("pre run code...");
242-
use crate::helper::{code_path, test_cases_path};
244+
use crate::helper::code_path;
243245
use std::fs::File;
244246
use std::io::Read;
245247

@@ -256,30 +258,48 @@ impl Cache {
256258
let mut code: String = "".to_string();
257259

258260
let maybe_file_testcases: Option<String> = test_cases_path(&p)
259-
.map(|filename| {
261+
.map(|file_name| {
260262
let mut tests = "".to_string();
261-
File::open(filename)
263+
File::open(file_name)
262264
.and_then(|mut file_descriptor| file_descriptor.read_to_string(&mut tests))
263265
.map(|_| Some(tests))
264266
.unwrap_or(None)
265267
})
266268
.unwrap_or(None);
267269

270+
let maybe_all_testcases: Option<String> = if d.all_cases.is_empty() {
271+
None
272+
} else {
273+
Some(d.all_cases.to_string())
274+
};
275+
268276
// Takes test cases using following priority
269277
// 1. cli parameter
270-
// 2. test cases from the file
271-
// 3. sample test case from the task
272-
let testcase = testcase.or(maybe_file_testcases).unwrap_or(d.case);
278+
// 2. if test cases file exist, use the file test cases(user can edit it)
279+
// 3. test cases from problem desc all test cases
280+
// 4. sample test case from the task
281+
let test_case = test_case
282+
.or(maybe_file_testcases)
283+
.or(maybe_all_testcases)
284+
.unwrap_or(d.case);
273285

274286
File::open(code_path(&p, None)?)?.read_to_string(&mut code)?;
275287

288+
let begin = code.find(CODE_START).unwrap_or(0);
289+
let end = code.find(CODE_END).unwrap_or(code.len());
290+
let code = if let Some(solution) = code.get(begin..end) {
291+
solution.to_string()
292+
} else {
293+
code
294+
};
295+
276296
json.insert("lang", conf.code.lang.to_string());
277297
json.insert("question_id", p.id.to_string());
278298
json.insert("typed_code", code);
279299

280300
// pass manually data
281301
json.insert("name", p.name.to_string());
282-
json.insert("data_input", testcase);
302+
json.insert("data_input", test_case);
283303

284304
let url = match run {
285305
Run::Test => conf
@@ -315,7 +335,7 @@ impl Cache {
315335
async fn recur_verify(&self, rid: String) -> Result<VerifyResult, Error> {
316336
use std::time::Duration;
317337

318-
trace!("Run veriy recursion...");
338+
trace!("Run verify recursion...");
319339
std::thread::sleep(Duration::from_micros(3000));
320340

321341
let json: VerifyResult = self
@@ -330,10 +350,10 @@ impl Cache {
330350
&self,
331351
rfid: i32,
332352
run: Run,
333-
testcase: Option<String>,
353+
test_case: Option<String>,
334354
) -> Result<VerifyResult, Error> {
335355
trace!("Exec problem filter —— Test or Submit");
336-
let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, testcase).await?;
356+
let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, test_case).await?;
337357
trace!("Pre run code result {:?}, {:?}, {:?}", json, url, refer);
338358

339359
let run_res: RunCode = self

‎src/cache/models.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ pub struct Problem {
3030
pub desc: String,
3131
}
3232

33+
impl Problem {
34+
fn display_level(&self) -> &str {
35+
match self.level {
36+
1 => "Easy",
37+
2 => "Medium",
38+
3 => "Hard",
39+
_ => "Unknown",
40+
}
41+
}
42+
pub fn desc_comment(&self) -> String {
43+
let mut res = String::new();
44+
res += format!("// Category: {}\n", self.category).as_str();
45+
res += format!("// Level: {}\n", self.display_level(),).as_str();
46+
res += format!("// Percent: {}%\n\n", self.percent).as_str();
47+
48+
res + "\n"
49+
}
50+
}
51+
3352
static DONE: &str = " ✔";
3453
static ETC: &str = "...";
3554
static LOCK: &str = "🔒";
@@ -124,9 +143,20 @@ pub struct Question {
124143
pub t_content: String,
125144
}
126145

127-
impl std::fmt::Display for Question {
128-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129-
write!(f, "{}", &self.content.render())
146+
impl Question {
147+
pub fn desc(&self) -> String {
148+
self.content.render()
149+
}
150+
151+
pub fn desc_comment(&self) -> String {
152+
let desc = self.content.render();
153+
154+
let mut res = desc
155+
.lines()
156+
.fold("/*\n".to_string(), |acc, e| acc + " * " + e + "\n");
157+
res += " */\n";
158+
159+
res
130160
}
131161
}
132162

‎src/cache/parser.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ pub fn desc(q: &mut Question, v: Value) -> Option<bool> {
5050
stats: serde_json::from_str(o.get("stats")?.as_str()?).ok()?,
5151
defs: serde_json::from_str(o.get("codeDefinition")?.as_str()?).ok()?,
5252
case: o.get("sampleTestCase")?.as_str()?.to_string(),
53-
all_cases: o.get("exampleTestcases")
54-
.unwrap_or(o.get("sampleTestCase")?) // soft fail to the sampleTestCase
55-
.as_str()?
56-
.to_string(),
53+
all_cases: o
54+
.get("exampleTestcases")
55+
.unwrap_or(o.get("sampleTestCase")?) // soft fail to the sampleTestCase
56+
.as_str()?
57+
.to_string(),
5758
metadata: serde_json::from_str(o.get("metaData")?.as_str()?).ok()?,
5859
test: o.get("enableRunCode")?.as_bool()?,
5960
t_content: o
@@ -85,29 +86,35 @@ pub fn tags(v: Value) -> Option<Vec<String>> {
8586
Some(res)
8687
}
8788

88-
/// daily parser
89+
/// daily parser
8990
pub fn daily(v: Value) -> Option<i32> {
9091
trace!("Parse daily...");
9192
v.as_object()?
92-
.get("data")?.as_object()?
93-
.get("activeDailyCodingChallengeQuestion")?.as_object()?
94-
.get("question")?.as_object()?
95-
.get("questionFrontendId")?.as_str()?
96-
.parse().ok()
93+
.get("data")?
94+
.as_object()?
95+
.get("activeDailyCodingChallengeQuestion")?
96+
.as_object()?
97+
.get("question")?
98+
.as_object()?
99+
.get("questionFrontendId")?
100+
.as_str()?
101+
.parse()
102+
.ok()
97103
}
98104

99105
/// user parser
100-
pub fn user(v: Value) -> Option<Option<(String,bool)>> {
106+
pub fn user(v: Value) -> Option<Option<(String,bool)>> {
101107
// None => error while parsing
102108
// Some(None) => User not found
103109
// Some("...") => username
104-
let user = v.as_object()?.get("data")?
105-
.as_object()?.get("user")?;
106-
if *user == Value::Null { return Some(None) }
110+
let user = v.as_object()?.get("data")?.as_object()?.get("user")?;
111+
if *user == Value::Null {
112+
return Some(None);
113+
}
107114
let user = user.as_object()?;
108115
Some(Some((
109116
user.get("username")?.as_str()?.to_owned(),
110-
user.get("isCurrentUserPremium")?.as_bool()?
117+
user.get("isCurrentUserPremium")?.as_bool()?,
111118
)))
112119
}
113120

‎src/cfg.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::Error;
99
use serde::{Deserialize, Serialize};
1010
use std::{collections::HashMap, fs, path::PathBuf};
1111

12-
const DEFAULT_CONFIG: &str = r#"
12+
const DEFAULT_CONFIG: &str = r##"
1313
# usually you don't wanna change those
1414
[sys]
1515
categories = [
@@ -65,11 +65,12 @@ csrf = ""
6565
session = ""
6666
6767
[storage]
68-
cache = "Problems"
69-
code = "code"
7068
root = "~/.leetcode"
69+
cache = "Problems"
7170
scripts = "scripts"
72-
"#;
71+
# absolutely path for the code, other use root as parent dir
72+
code = "code"
73+
"##;
7374

7475
/// Sync with `~/.leetcode/config.toml`
7576
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -169,10 +170,9 @@ impl Storage {
169170

170171
/// get code path
171172
pub fn code(&self) -> Result<String, crate::Error> {
172-
let root = &self.root()?;
173-
let p = PathBuf::from(root).join(&self.code);
173+
let p = PathBuf::from(&self.code);
174174
if !PathBuf::from(&p).exists() {
175-
std::fs::create_dir(&p)?
175+
fs::create_dir(&p)?
176176
}
177177

178178
Ok(p.to_string_lossy().to_string())

‎src/cli.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
err::Error,
88
flag::{Debug, Flag},
99
};
10-
use clap::{crate_name, crate_version,App,AppSettings};
10+
use clap::{crate_name, crate_version};
1111

1212
/// This should be called before calling any cli method or printing any output.
1313
pub fn reset_signal_pipe_handler() {
@@ -24,8 +24,8 @@ pub fn reset_signal_pipe_handler() {
2424

2525
/// Get maches
2626
pub async fn main() -> Result<(), Error> {
27-
let _ = reset_signal_pipe_handler();
28-
let m = App::new(crate_name!())
27+
reset_signal_pipe_handler();
28+
let m = clap::Command::new(crate_name!())
2929
.version(crate_version!())
3030
.about("May the Code be with You 👻")
3131
.subcommands(vec![
@@ -38,10 +38,10 @@ pub async fn main() -> Result<(), Error> {
3838
TestCommand::usage().display_order(7),
3939
])
4040
.arg(Debug::usage())
41-
.setting(AppSettings::ArgRequiredElseHelp)
41+
.arg_required_else_help(true)
4242
.get_matches();
4343

44-
if m.is_present("debug") {
44+
if m.contains_id("debug") {
4545
Debug::handler()?;
4646
} else {
4747
env_logger::Builder::from_env(env_logger::Env::new().default_filter_or("info"))

‎src/cmds/data.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use super::Command;
33
use crate::{cache::Cache, helper::Digit, Error};
44
use async_trait::async_trait;
5-
use clap::{Command as ClapCommand,Arg,ArgMatches};
5+
use clap::{Arg,ArgMatches,Command as ClapCommand};
66
use colored::Colorize;
77

88
/// Abstract `data` command
@@ -58,7 +58,8 @@ impl Command for DataCommand {
5858
let out = format!(
5959
" {}{}",
6060
Path::new(&path)
61-
.file_name().ok_or(Error::NoneError)?
61+
.file_name()
62+
.ok_or(Error::NoneError)?
6263
.to_string_lossy()
6364
.to_string()
6465
.digit(65 - (len.len() as i32))
@@ -72,13 +73,13 @@ impl Command for DataCommand {
7273
title.push_str(&"-".repeat(65));
7374

7475
let mut flags = 0;
75-
if m.is_present("delete") {
76+
if m.contains_id("delete") {
7677
flags += 1;
7778
cache.clean()?;
7879
println!("{}", "ok!".bright_green());
7980
}
8081

81-
if m.is_present("update") {
82+
if m.contains_id("update") {
8283
flags += 1;
8384
cache.update().await?;
8485
println!("{}", "ok!".bright_green());

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /