Skip to content

Feat: like vsc-leetcode-cli use @lc code=start and @lc code=end to pick the code #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 9, 2022
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ clap = { version = "3.2.8", features = ["cargo"] }
colored = "2.0.0"
dirs = "4.0.0"
env_logger = "0.9.0"
escaper = "0.1.1"
keyring = "1.1.2"
log = "0.4.17"
openssl = "0.10.40"
Expand All @@ -32,6 +31,7 @@ serde = { version = "1.0.138", features = ["derive"] }
serde_json = "1.0.82"
toml = "0.5.9"
regex = "1.5.6"
scraper = "0.13.0"

[dependencies.diesel]
version = "1.4.8"
Expand Down
46 changes: 33 additions & 13 deletions src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod sql;
use self::models::*;
use self::schemas::{problems::dsl::*, tags::dsl::*};
use self::sql::*;
use crate::cmds::{CODE_END, CODE_START};
use crate::helper::test_cases_path;
use crate::{cfg, err::Error, plugins::LeetCode};
use colored::Colorize;
use diesel::prelude::*;
Expand All @@ -26,7 +28,7 @@ pub enum Run {
Submit,
}

impl std::default::Default for Run {
impl Default for Run {
fn default() -> Self {
Run::Submit
}
Expand All @@ -37,7 +39,7 @@ impl std::default::Default for Run {
pub struct Cache(pub LeetCode);

impl Cache {
/// Ref to sqliteconnection
/// Ref to sqlite connection
fn conn(&self) -> Result<SqliteConnection, Error> {
Ok(conn(self.0.conf.storage.cache()?))
}
Expand Down Expand Up @@ -236,10 +238,10 @@ impl Cache {
&self,
run: Run,
rfid: i32,
testcase: Option<String>,
test_case: Option<String>,
) -> Result<(HashMap<&'static str, String>, [String; 2]), Error> {
trace!("pre run code...");
use crate::helper::{code_path, test_cases_path};
use crate::helper::code_path;
use std::fs::File;
use std::io::Read;

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

let maybe_file_testcases: Option<String> = test_cases_path(&p)
.map(|filename| {
.map(|file_name| {
let mut tests = "".to_string();
File::open(filename)
File::open(file_name)
.and_then(|mut file_descriptor| file_descriptor.read_to_string(&mut tests))
.map(|_| Some(tests))
.unwrap_or(None)
})
.unwrap_or(None);

let maybe_all_testcases: Option<String> = if d.all_cases.is_empty() {
None
} else {
Some(d.all_cases.to_string())
};

// Takes test cases using following priority
// 1. cli parameter
// 2. test cases from the file
// 3. sample test case from the task
let testcase = testcase.or(maybe_file_testcases).unwrap_or(d.case);
// 2. if test cases file exist, use the file test cases(user can edit it)
// 3. test cases from problem desc all test cases
// 4. sample test case from the task
let test_case = test_case
.or(maybe_file_testcases)
.or(maybe_all_testcases)
.unwrap_or(d.case);

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

let begin = code.find(CODE_START).unwrap_or(0);
let end = code.find(CODE_END).unwrap_or(code.len());
let code = if let Some(solution) = code.get(begin..end) {
solution.to_string()
} else {
code
};

json.insert("lang", conf.code.lang.to_string());
json.insert("question_id", p.id.to_string());
json.insert("typed_code", code);

// pass manually data
json.insert("name", p.name.to_string());
json.insert("data_input", testcase);
json.insert("data_input", test_case);

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

trace!("Run veriy recursion...");
trace!("Run verify recursion...");
std::thread::sleep(Duration::from_micros(3000));

let json: VerifyResult = self
Expand All @@ -330,10 +350,10 @@ impl Cache {
&self,
rfid: i32,
run: Run,
testcase: Option<String>,
test_case: Option<String>,
) -> Result<VerifyResult, Error> {
trace!("Exec problem filter —— Test or Submit");
let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, testcase).await?;
let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, test_case).await?;
trace!("Pre run code result {:?}, {:?}, {:?}", json, url, refer);

let run_res: RunCode = self
Expand Down
36 changes: 33 additions & 3 deletions src/cache/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ pub struct Problem {
pub desc: String,
}

impl Problem {
fn display_level(&self) -> &str {
match self.level {
1 => "Easy",
2 => "Medium",
3 => "Hard",
_ => "Unknown",
}
}
pub fn desc_comment(&self) -> String {
let mut res = String::new();
res += format!("// Category: {}\n", self.category).as_str();
res += format!("// Level: {}\n", self.display_level(),).as_str();
res += format!("// Percent: {}%\n\n", self.percent).as_str();

res + "\n"
}
}

static DONE: &str = " ✔";
static ETC: &str = "...";
static LOCK: &str = "🔒";
Expand Down Expand Up @@ -124,9 +143,20 @@ pub struct Question {
pub t_content: String,
}

impl std::fmt::Display for Question {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.content.render())
impl Question {
pub fn desc(&self) -> String {
self.content.render()
}

pub fn desc_comment(&self) -> String {
let desc = self.content.render();

let mut res = desc
.lines()
.fold("/*\n".to_string(), |acc, e| acc + " * " + e + "\n");
res += " */\n";

res
}
}

Expand Down
37 changes: 22 additions & 15 deletions src/cache/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ pub fn desc(q: &mut Question, v: Value) -> Option<bool> {
stats: serde_json::from_str(o.get("stats")?.as_str()?).ok()?,
defs: serde_json::from_str(o.get("codeDefinition")?.as_str()?).ok()?,
case: o.get("sampleTestCase")?.as_str()?.to_string(),
all_cases: o.get("exampleTestcases")
.unwrap_or(o.get("sampleTestCase")?) // soft fail to the sampleTestCase
.as_str()?
.to_string(),
all_cases: o
.get("exampleTestcases")
.unwrap_or(o.get("sampleTestCase")?) // soft fail to the sampleTestCase
.as_str()?
.to_string(),
metadata: serde_json::from_str(o.get("metaData")?.as_str()?).ok()?,
test: o.get("enableRunCode")?.as_bool()?,
t_content: o
Expand Down Expand Up @@ -85,29 +86,35 @@ pub fn tags(v: Value) -> Option<Vec<String>> {
Some(res)
}

/// daily parser
/// daily parser
pub fn daily(v: Value) -> Option<i32> {
trace!("Parse daily...");
v.as_object()?
.get("data")?.as_object()?
.get("activeDailyCodingChallengeQuestion")?.as_object()?
.get("question")?.as_object()?
.get("questionFrontendId")?.as_str()?
.parse().ok()
.get("data")?
.as_object()?
.get("activeDailyCodingChallengeQuestion")?
.as_object()?
.get("question")?
.as_object()?
.get("questionFrontendId")?
.as_str()?
.parse()
.ok()
}

/// user parser
pub fn user(v: Value) -> Option<Option<(String,bool)>> {
pub fn user(v: Value) -> Option<Option<(String, bool)>> {
// None => error while parsing
// Some(None) => User not found
// Some("...") => username
let user = v.as_object()?.get("data")?
.as_object()?.get("user")?;
if *user == Value::Null { return Some(None) }
let user = v.as_object()?.get("data")?.as_object()?.get("user")?;
if *user == Value::Null {
return Some(None);
}
let user = user.as_object()?;
Some(Some((
user.get("username")?.as_str()?.to_owned(),
user.get("isCurrentUserPremium")?.as_bool()?
user.get("isCurrentUserPremium")?.as_bool()?,
)))
}

Expand Down
14 changes: 7 additions & 7 deletions src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::Error;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::PathBuf};

const DEFAULT_CONFIG: &str = r#"
const DEFAULT_CONFIG: &str = r##"
# usually you don't wanna change those
[sys]
categories = [
Expand Down Expand Up @@ -65,11 +65,12 @@ csrf = ""
session = ""

[storage]
cache = "Problems"
code = "code"
root = "~/.leetcode"
cache = "Problems"
scripts = "scripts"
"#;
# absolutely path for the code, other use root as parent dir
code = "code"
"##;

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

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

Ok(p.to_string_lossy().to_string())
Expand Down
10 changes: 5 additions & 5 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
err::Error,
flag::{Debug, Flag},
};
use clap::{crate_name, crate_version, App, AppSettings};
use clap::{crate_name, crate_version};

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

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

if m.is_present("debug") {
if m.contains_id("debug") {
Debug::handler()?;
} else {
env_logger::Builder::from_env(env_logger::Env::new().default_filter_or("info"))
Expand Down
9 changes: 5 additions & 4 deletions src/cmds/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use super::Command;
use crate::{cache::Cache, helper::Digit, Error};
use async_trait::async_trait;
use clap::{Command as ClapCommand, Arg, ArgMatches};
use clap::{Arg, ArgMatches, Command as ClapCommand};
use colored::Colorize;

/// Abstract `data` command
Expand Down Expand Up @@ -58,7 +58,8 @@ impl Command for DataCommand {
let out = format!(
" {}{}",
Path::new(&path)
.file_name().ok_or(Error::NoneError)?
.file_name()
.ok_or(Error::NoneError)?
.to_string_lossy()
.to_string()
.digit(65 - (len.len() as i32))
Expand All @@ -72,13 +73,13 @@ impl Command for DataCommand {
title.push_str(&"-".repeat(65));

let mut flags = 0;
if m.is_present("delete") {
if m.contains_id("delete") {
flags += 1;
cache.clean()?;
println!("{}", "ok!".bright_green());
}

if m.is_present("update") {
if m.contains_id("update") {
flags += 1;
cache.update().await?;
println!("{}", "ok!".bright_green());
Expand Down
Loading