From 1918ae435ac0d6221fed23ec97141a20a6b488e8 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 14 Aug 2022 18:16:50 +0800 Subject: [PATCH 01/12] feat(config): add module config instead of cfg.rs --- src/cache/mod.rs | 4 ++-- src/cache/models.rs | 1 + src/{cfg.rs => config/mod.rs} | 29 +++-------------------------- src/err.rs | 2 +- src/helper.rs | 6 +++--- src/lib.rs | 4 ++-- src/plugins/chrome.rs | 2 +- src/plugins/leetcode.rs | 4 ++-- 8 files changed, 15 insertions(+), 37 deletions(-) rename src/{cfg.rs => config/mod.rs} (93%) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 345b3ea..a39ad3e 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -7,7 +7,7 @@ use self::models::*; use self::schemas::{problems::dsl::*, tags::dsl::*}; use self::sql::*; use crate::helper::test_cases_path; -use crate::{cfg, err::Error, plugins::LeetCode}; +use crate::{config, err::Error, plugins::LeetCode}; use colored::Colorize; use diesel::prelude::*; use reqwest::Response; @@ -395,7 +395,7 @@ impl Cache { /// New cache pub fn new() -> Result { - let conf = cfg::locate()?; + let conf = config::locate()?; let c = conn(conf.storage.cache()?); diesel::sql_query(CREATE_PROBLEMS_IF_NOT_EXISTS).execute(&c)?; diesel::sql_query(CREATE_TAGS_IF_NOT_EXISTS).execute(&c)?; diff --git a/src/cache/models.rs b/src/cache/models.rs index 0a9a6ab..2caf94f 100644 --- a/src/cache/models.rs +++ b/src/cache/models.rs @@ -39,6 +39,7 @@ impl Problem { _ => "Unknown", } } + pub fn desc_comment(&self, conf: &Config) -> String { let mut res = String::new(); let comment_leading = &conf.code.comment_leading; diff --git a/src/cfg.rs b/src/config/mod.rs similarity index 93% rename from src/cfg.rs rename to src/config/mod.rs index 184e3e0..451e094 100644 --- a/src/cfg.rs +++ b/src/config/mod.rs @@ -19,25 +19,6 @@ categories = [ "shell" ] -langs = [ - "bash", - "c", - "cpp", - "csharp", - "golang", - "java", - "javascript", - "kotlin", - "mysql", - "php", - "python", - "python3", - "ruby", - "rust", - "scala", - "swift" -] - [sys.urls] base = "https://leetcode.com" graphql = "https://leetcode.com/graphql" @@ -74,8 +55,8 @@ session = "" root = "~/.leetcode" scripts = "scripts" code = "code" -# absolutely path for the cache, other use root as parent dir -cache = "~/.cache/leetcode" +# Relative path for the cache, otherwise use root as parent dir +cache = "Problems" "##; /// Sync with `~/.leetcode/config.toml` @@ -109,7 +90,6 @@ pub struct Cookies { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Sys { pub categories: Vec, - pub langs: Vec, pub urls: HashMap, } @@ -131,10 +111,7 @@ pub struct Urls { pub favorite_delete: String, } -/// default editor and langs -/// -/// + support editor: [emacs, vim] -/// + support langs: all in config +/// Code config #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Code { pub editor: String, diff --git a/src/err.rs b/src/err.rs index 5f007e9..ffffe77 100644 --- a/src/err.rs +++ b/src/err.rs @@ -1,6 +1,6 @@ //! Errors in leetcode-cli -use crate::cfg::{root, DEFAULT_CONFIG}; use crate::cmds::{Command, DataCommand}; +use crate::config::{root, DEFAULT_CONFIG}; use colored::Colorize; use std::fmt; diff --git a/src/helper.rs b/src/helper.rs index 60efcd8..7a1b40b 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -190,7 +190,7 @@ mod file { /// Generate test cases path by fid pub fn test_cases_path(problem: &Problem) -> Result { - let conf = crate::cfg::locate()?; + let conf = crate::config::locate()?; let mut path = format!("{}/{}.tests.dat", conf.storage.code()?, conf.code.pick); path = path.replace("${fid}", &problem.fid.to_string()); @@ -200,7 +200,7 @@ mod file { /// Generate code path by fid pub fn code_path(problem: &Problem, l: Option) -> Result { - let conf = crate::cfg::locate()?; + let conf = crate::config::locate()?; let mut lang = conf.code.lang; if l.is_some() { lang = l.ok_or(Error::NoneError)?; @@ -223,7 +223,7 @@ mod file { pub fn load_script(module: &str) -> Result { use std::fs::File; use std::io::Read; - let conf = crate::cfg::locate()?; + let conf = crate::config::locate()?; let mut script = "".to_string(); File::open(format!("{}/{}.py", conf.storage.scripts()?, module))? .read_to_string(&mut script)?; diff --git a/src/lib.rs b/src/lib.rs index e97fb6e..4bc2a53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,9 +235,9 @@ extern crate diesel; // show docs pub mod cache; -pub mod cfg; pub mod cli; pub mod cmds; +pub mod config; pub mod err; pub mod flag; pub mod helper; @@ -247,5 +247,5 @@ pub mod pym; // re-exports pub use cache::Cache; -pub use cfg::Config; +pub use config::Config; pub use err::Error; diff --git a/src/plugins/chrome.rs b/src/plugins/chrome.rs index 20ba976..1105f48 100644 --- a/src/plugins/chrome.rs +++ b/src/plugins/chrome.rs @@ -41,7 +41,7 @@ impl std::string::ToString for Ident { /// Get cookies from chrome storage pub fn cookies() -> Result { - let ccfg = crate::cfg::locate()?.cookies; + let ccfg = crate::config::locate()?.cookies; if !ccfg.csrf.is_empty() && !ccfg.session.is_empty() { return Ok(Ident { csrf: ccfg.csrf, diff --git a/src/plugins/leetcode.rs b/src/plugins/leetcode.rs index e06627f..e8ccb91 100644 --- a/src/plugins/leetcode.rs +++ b/src/plugins/leetcode.rs @@ -1,6 +1,6 @@ use self::req::{Json, Mode, Req}; use crate::{ - cfg::{self, Config}, + config::{self, Config}, err::Error, plugins::chrome, }; @@ -36,7 +36,7 @@ impl LeetCode { /// New LeetCode client pub fn new() -> Result { - let conf = cfg::locate()?; + let conf = config::locate()?; let cookies = chrome::cookies()?; let default_headers = LeetCode::headers( HeaderMap::new(), From 5a9322f6c80de24914da2cb5558251549a727b26 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 14 Aug 2022 20:56:40 +0800 Subject: [PATCH 02/12] feat(config): refactor sys --- src/cache/mod.rs | 25 ++--------- src/config/mod.rs | 64 ++++------------------------ src/config/sys.rs | 93 +++++++++++++++++++++++++++++++++++++++++ src/plugins/leetcode.rs | 39 +++++------------ 4 files changed, 114 insertions(+), 107 deletions(-) create mode 100644 src/config/sys.rs diff --git a/src/cache/mod.rs b/src/cache/mod.rs index a39ad3e..6a70643 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -307,33 +307,14 @@ impl Cache { json.insert("data_input", test_case); let url = match run { - Run::Test => conf - .sys - .urls - .get("test") - .ok_or(Error::NoneError)? - .replace("$slug", &p.slug), + Run::Test => conf.sys.urls.test(&p.slug), Run::Submit => { json.insert("judge_type", "large".to_string()); - conf.sys - .urls - .get("submit") - .ok_or(Error::NoneError)? - .replace("$slug", &p.slug) + conf.sys.urls.submit(&p.slug) } }; - Ok(( - json, - [ - url, - conf.sys - .urls - .get("problems") - .ok_or(Error::NoneError)? - .replace("$slug", &p.slug), - ], - )) + Ok((json, [url, conf.sys.urls.problem(&p.slug)])) } /// TODO: The real delay diff --git a/src/config/mod.rs b/src/config/mod.rs index 451e094..8081966 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,36 +5,13 @@ //! //! + Edit leetcode.toml at `~/.leetcode/leetcode.toml` directly //! + Use `leetcode config` to update it -use crate::Error; +use crate::{config::sys::Sys, Error}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs, path::PathBuf}; - -pub const DEFAULT_CONFIG: &str = r##" -# usually you don't wanna change those -[sys] -categories = [ - "algorithms", - "concurrency", - "database", - "shell" -] - -[sys.urls] -base = "https://leetcode.com" -graphql = "https://leetcode.com/graphql" -login = "https://leetcode.com/accounts/login/" -problems = "https://leetcode.com/api/problems/$category/" -problem = "https://leetcode.com/problems/$slug/description/" -tag = "https://leetcode.com/tag/$slug/" -test = "https://leetcode.com/problems/$slug/interpret_solution/" -session = "https://leetcode.com/session/" -submit = "https://leetcode.com/problems/$slug/submit/" -submissions = "https://leetcode.com/api/submissions/$slug" -submission = "https://leetcode.com/submissions/detail/$id/" -verify = "https://leetcode.com/submissions/detail/$id/check/" -favorites = "https://leetcode.com/list/api/questions" -favorite_delete = "https://leetcode.com/list/api/questions/$hash/$id" +use std::{fs, path::PathBuf}; +mod sys; + +pub const DEFAULT_CONFIG: &str = r#" [code] editor = "vim" lang = "rust" @@ -55,13 +32,13 @@ session = "" root = "~/.leetcode" scripts = "scripts" code = "code" -# Relative path for the cache, otherwise use root as parent dir cache = "Problems" -"##; +"#; /// Sync with `~/.leetcode/config.toml` #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Config { + #[serde(skip)] pub sys: Sys, pub code: Code, pub cookies: Cookies, @@ -86,31 +63,6 @@ pub struct Cookies { pub session: String, } -/// System settings, for leetcode api mainly -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Sys { - pub categories: Vec, - pub urls: HashMap, -} - -/// Leetcode API -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Urls { - pub base: String, - pub graphql: String, - pub login: String, - pub problems: String, - pub problem: String, - pub test: String, - pub session: String, - pub submit: String, - pub submissions: String, - pub submission: String, - pub verify: String, - pub favorites: String, - pub favorite_delete: String, -} - /// Code config #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Code { @@ -197,7 +149,7 @@ impl Storage { pub fn locate() -> Result { let conf = root()?.join("leetcode.toml"); if !conf.is_file() { - fs::write(&conf, &DEFAULT_CONFIG[1..])?; + fs::write(&conf, &DEFAULT_CONFIG.trim())?; } let s = fs::read_to_string(&conf)?; diff --git a/src/config/sys.rs b/src/config/sys.rs new file mode 100644 index 0000000..4386bd9 --- /dev/null +++ b/src/config/sys.rs @@ -0,0 +1,93 @@ +//! System section +//! +//! This section is a set of constants after #88 + +const CATEGORIES: [&str; 4] = ["algorithms", "concurrency", "database", "shell"]; + +/// Leetcode API +#[derive(Clone, Debug)] +pub struct Urls { + pub base: String, + pub graphql: String, + pub login: String, + pub problems: String, + pub problem: String, + pub tag: String, + pub test: String, + pub session: String, + pub submit: String, + pub submissions: String, + pub submission: String, + pub verify: String, + pub favorites: String, + pub favorite_delete: String, +} + +impl Default for Urls { + fn default() -> Self { + Self { + base: "https://leetcode.com".into(), + graphql: "https://leetcode.com/graphql".into(), + login: "https://leetcode.com/accounts/login/".into(), + problems: "https://leetcode.com/api/problems/$category/".into(), + problem: "https://leetcode.com/problems/$slug/description/".into(), + tag: "https://leetcode.com/tag/$slug/".into(), + test: "https://leetcode.com/problems/$slug/interpret_solution/".into(), + session: "https://leetcode.com/session/".into(), + submit: "https://leetcode.com/problems/$slug/submit/".into(), + submissions: "https://leetcode.com/submissions/detail/$id/".into(), + submission: "https://leetcode.com/submissions/detail/$id/".into(), + verify: "https://leetcode.com/submissions/detail/$id/check/".into(), + favorites: "https://leetcode.com/list/api/questions".into(), + favorite_delete: "https://leetcode.com/list/api/questions/$hash/$id".into(), + } + } +} + +impl Urls { + /// problem url with specific `$slug` + pub fn problem(&self, slug: &str) -> String { + self.problem.replace("$slug", slug) + } + + /// problems url with specific `$category` + pub fn problems(&self, category: &str) -> String { + self.problem.replace("$category", category) + } + + /// submit url with specific `$slug` + pub fn submit(&self, slug: &str) -> String { + self.submit.replace("$slug", slug) + } + + /// tag url with specific `$slug` + pub fn tag(&self, slug: &str) -> String { + self.tag.replace("$slug", slug) + } + + /// test url with specific `$slug` + pub fn test(&self, slug: &str) -> String { + self.test.replace("$slug", slug) + } + + /// verify url with specific `$id` + pub fn verify(&self, id: &str) -> String { + self.verify.replace("$id", id) + } +} + +/// System settings, for leetcode api mainly +#[derive(Clone, Debug)] +pub struct Sys { + pub categories: Vec, + pub urls: Urls, +} + +impl Default for Sys { + fn default() -> Self { + Self { + categories: CATEGORIES.into_iter().map(|s| s.into()).collect(), + urls: Default::default(), + } + } +} diff --git a/src/plugins/leetcode.rs b/src/plugins/leetcode.rs index e8ccb91..1e7b17f 100644 --- a/src/plugins/leetcode.rs +++ b/src/plugins/leetcode.rs @@ -44,7 +44,7 @@ impl LeetCode { ("Cookie", cookies.to_string().as_str()), ("x-csrftoken", &cookies.csrf), ("x-requested-with", "XMLHttpRequest"), - ("Origin", &conf.sys.urls["base"]), + ("Origin", &conf.sys.urls.base), ], )?; @@ -68,13 +68,7 @@ impl LeetCode { /// Get category problems pub async fn get_category_problems(self, category: &str) -> Result { trace!("Requesting {} problems...", &category); - let url = &self - .conf - .sys - .urls - .get("problems") - .ok_or(Error::NoneError)? - .replace("$category", category); + let url = &self.conf.sys.urls.problems(category); Req { default_headers: self.default_headers, @@ -91,7 +85,7 @@ impl LeetCode { pub async fn get_question_ids_by_tag(self, slug: &str) -> Result { trace!("Requesting {} ref problems...", &slug); - let url = &self.conf.sys.urls.get("graphql").ok_or(Error::NoneError)?; + let url = &self.conf.sys.urls.graphql; let mut json: Json = HashMap::new(); json.insert("operationName", "getTopicTag".to_string()); json.insert("variables", r#"{"slug": "$slug"}"#.replace("$slug", slug)); @@ -111,9 +105,7 @@ impl LeetCode { Req { default_headers: self.default_headers, - refer: Some( - (self.conf.sys.urls.get("tag").ok_or(Error::NoneError)?).replace("$slug", slug), - ), + refer: Some(self.conf.sys.urls.tag(slug)), info: false, json: Some(json), mode: Mode::Post, @@ -126,7 +118,7 @@ impl LeetCode { pub async fn get_user_info(self) -> Result { trace!("Requesting user info..."); - let url = &self.conf.sys.urls.get("graphql").ok_or(Error::NoneError)?; + let url = &self.conf.sys.urls.graphql; let mut json: Json = HashMap::new(); json.insert("operationName", "a".to_string()); json.insert( @@ -156,7 +148,7 @@ impl LeetCode { /// Get daily problem pub async fn get_question_daily(self) -> Result { trace!("Requesting daily problem..."); - let url = &self.conf.sys.urls.get("graphql").ok_or(Error::NoneError)?; + let url = &self.conf.sys.urls.graphql; let mut json: Json = HashMap::new(); json.insert("operationName", "daily".to_string()); json.insert( @@ -189,13 +181,7 @@ impl LeetCode { /// Get specific problem detail pub async fn get_question_detail(self, slug: &str) -> Result { trace!("Requesting {} detail...", &slug); - let refer = self - .conf - .sys - .urls - .get("problems") - .ok_or(Error::NoneError)? - .replace("$slug", slug); + let refer = self.conf.sys.urls.problem(slug); let mut json: Json = HashMap::new(); json.insert( "query", @@ -230,7 +216,7 @@ impl LeetCode { json: Some(json), mode: Mode::Post, name: "get_problem_detail", - url: (&self.conf.sys.urls["graphql"]).to_string(), + url: self.conf.sys.urls.graphql.into(), } .send(&self.client) .await @@ -255,13 +241,8 @@ impl LeetCode { /// Get the result of submission / testing pub async fn verify_result(self, id: String) -> Result { trace!("Verifying result..."); - let url = self - .conf - .sys - .urls - .get("verify") - .ok_or(Error::NoneError)? - .replace("$id", &id); + let url = self.conf.sys.urls.verify(&id); + Req { default_headers: self.default_headers, refer: None, From 9bd3a4712ba717f4d7107ff2b17606b3b2c127d1 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 14 Aug 2022 21:15:39 +0800 Subject: [PATCH 03/12] chore(config): wrap util functions in to Config --- src/cache/mod.rs | 4 +- src/config/code.rs | 19 ++++++ src/config/cookies.rs | 9 +++ src/config/mod.rs | 139 ++++++++-------------------------------- src/config/storage.rs | 68 ++++++++++++++++++++ src/err.rs | 4 +- src/helper.rs | 6 +- src/plugins/chrome.rs | 2 +- src/plugins/leetcode.rs | 2 +- 9 files changed, 133 insertions(+), 120 deletions(-) create mode 100644 src/config/code.rs create mode 100644 src/config/cookies.rs create mode 100644 src/config/storage.rs diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 6a70643..fe88cfc 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -7,7 +7,7 @@ use self::models::*; use self::schemas::{problems::dsl::*, tags::dsl::*}; use self::sql::*; use crate::helper::test_cases_path; -use crate::{config, err::Error, plugins::LeetCode}; +use crate::{config::Config, err::Error, plugins::LeetCode}; use colored::Colorize; use diesel::prelude::*; use reqwest::Response; @@ -376,7 +376,7 @@ impl Cache { /// New cache pub fn new() -> Result { - let conf = config::locate()?; + let conf = Config::locate()?; let c = conn(conf.storage.cache()?); diesel::sql_query(CREATE_PROBLEMS_IF_NOT_EXISTS).execute(&c)?; diesel::sql_query(CREATE_TAGS_IF_NOT_EXISTS).execute(&c)?; diff --git a/src/config/code.rs b/src/config/code.rs new file mode 100644 index 0000000..1f8f49e --- /dev/null +++ b/src/config/code.rs @@ -0,0 +1,19 @@ +//! Code in config +use serde::{Deserialize, Serialize}; + +/// Code config +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Code { + pub editor: String, + #[serde(rename(serialize = "editor-args", deserialize = "editor-args"))] + pub editor_args: Option>, + pub edit_code_marker: bool, + pub start_marker: String, + pub end_marker: String, + pub comment_problem_desc: bool, + pub comment_leading: String, + pub test: bool, + pub lang: String, + pub pick: String, + pub submission: String, +} diff --git a/src/config/cookies.rs b/src/config/cookies.rs new file mode 100644 index 0000000..0c75a90 --- /dev/null +++ b/src/config/cookies.rs @@ -0,0 +1,9 @@ +//! Cookies in config +use serde::{Deserialize, Serialize}; + +/// Cookies settings +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Cookies { + pub csrf: String, + pub session: String, +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 8081966..4eb60f9 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,10 +5,16 @@ //! //! + Edit leetcode.toml at `~/.leetcode/leetcode.toml` directly //! + Use `leetcode config` to update it -use crate::{config::sys::Sys, Error}; +use crate::{ + config::{code::Code, cookies::Cookies, storage::Storage, sys::Sys}, + Error, +}; use serde::{Deserialize, Serialize}; -use std::{fs, path::PathBuf}; +use std::fs; +mod code; +mod cookies; +mod storage; mod sys; pub const DEFAULT_CONFIG: &str = r#" @@ -46,123 +52,34 @@ pub struct Config { } impl Config { - /// Sync new config to config.toml - pub fn sync(&self) -> Result<(), Error> { - let home = dirs::home_dir().ok_or(Error::NoneError)?; - let conf = home.join(".leetcode/leetcode.toml"); - fs::write(conf, toml::ser::to_string_pretty(&self)?)?; - - Ok(()) - } -} - -/// Cookie settings -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Cookies { - pub csrf: String, - pub session: String, -} - -/// Code config -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Code { - pub editor: String, - #[serde(rename(serialize = "editor-args", deserialize = "editor-args"))] - pub editor_args: Option>, - pub edit_code_marker: bool, - pub start_marker: String, - pub end_marker: String, - pub comment_problem_desc: bool, - pub comment_leading: String, - pub test: bool, - pub lang: String, - pub pick: String, - pub submission: String, -} - -/// Locate code files -/// -/// + cache -> the path to cache -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Storage { - cache: String, - code: String, - root: String, - scripts: Option, -} - -impl Storage { - /// convert root path - pub fn root(&self) -> Result { - let home = dirs::home_dir() - .ok_or(Error::NoneError)? - .to_string_lossy() - .to_string(); - let path = self.root.replace('~', &home); - Ok(path) - } - - /// get cache path - pub fn cache(&self) -> Result { - let home = dirs::home_dir() - .ok_or(Error::NoneError)? - .to_string_lossy() - .to_string(); - let path = PathBuf::from(self.cache.replace('~', &home)); - if !path.is_dir() { - info!("Generate cache dir at {:?}.", &path); - fs::DirBuilder::new().recursive(true).create(&path)?; - } - - Ok(path.join("Problems").to_string_lossy().to_string()) - } - - /// get code path - pub fn code(&self) -> Result { - let root = &self.root()?; - let p = PathBuf::from(root).join(&self.code); - if !PathBuf::from(&p).exists() { - fs::create_dir(&p)? + /// Locate lc's config file + pub fn locate() -> Result { + let conf = Self::root()?.join("leetcode.toml"); + if !conf.is_file() { + fs::write(&conf, &DEFAULT_CONFIG.trim())?; } - Ok(p.to_string_lossy().to_string()) + let s = fs::read_to_string(&conf)?; + Ok(toml::from_str::(&s)?) } - /// get scripts path - pub fn scripts(mut self) -> Result { - let root = &self.root()?; - if self.scripts.is_none() { - let tmp = toml::from_str::(DEFAULT_CONFIG)?; - self.scripts = Some(tmp.storage.scripts.ok_or(Error::NoneError)?); - } - - let p = PathBuf::from(root).join(&self.scripts.ok_or(Error::NoneError)?); - if !PathBuf::from(&p).exists() { - std::fs::create_dir(&p)? + /// Get root path of leetcode-cli + pub fn root() -> Result { + let dir = dirs::home_dir().ok_or(Error::NoneError)?.join(".leetcode"); + if !dir.is_dir() { + info!("Generate root dir at {:?}.", &dir); + fs::DirBuilder::new().recursive(true).create(&dir)?; } - Ok(p.to_string_lossy().to_string()) + Ok(dir) } -} -/// Locate lc's config file -pub fn locate() -> Result { - let conf = root()?.join("leetcode.toml"); - if !conf.is_file() { - fs::write(&conf, &DEFAULT_CONFIG.trim())?; - } - - let s = fs::read_to_string(&conf)?; - Ok(toml::from_str::(&s)?) -} + /// Sync new config to config.toml + pub fn sync(&self) -> Result<(), Error> { + let home = dirs::home_dir().ok_or(Error::NoneError)?; + let conf = home.join(".leetcode/leetcode.toml"); + fs::write(conf, toml::ser::to_string_pretty(&self)?)?; -/// Get root path of leetcode-cli -pub fn root() -> Result { - let dir = dirs::home_dir().ok_or(Error::NoneError)?.join(".leetcode"); - if !dir.is_dir() { - info!("Generate root dir at {:?}.", &dir); - fs::DirBuilder::new().recursive(true).create(&dir)?; + Ok(()) } - - Ok(dir) } diff --git a/src/config/storage.rs b/src/config/storage.rs new file mode 100644 index 0000000..6a0284e --- /dev/null +++ b/src/config/storage.rs @@ -0,0 +1,68 @@ +//! Storage in config. +use crate::Error; +use serde::{Deserialize, Serialize}; +use std::{fs, path::PathBuf}; + +/// Locate code files +/// +/// + cache -> the path to cache +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Storage { + cache: String, + code: String, + root: String, + scripts: Option, +} + +impl Storage { + /// convert root path + pub fn root(&self) -> Result { + let home = dirs::home_dir() + .ok_or(Error::NoneError)? + .to_string_lossy() + .to_string(); + let path = self.root.replace('~', &home); + Ok(path) + } + + /// get cache path + pub fn cache(&self) -> Result { + let home = dirs::home_dir() + .ok_or(Error::NoneError)? + .to_string_lossy() + .to_string(); + let path = PathBuf::from(self.cache.replace('~', &home)); + if !path.is_dir() { + info!("Generate cache dir at {:?}.", &path); + fs::DirBuilder::new().recursive(true).create(&path)?; + } + + Ok(path.join("Problems").to_string_lossy().to_string()) + } + + /// get code path + pub fn code(&self) -> Result { + let root = &self.root()?; + let p = PathBuf::from(root).join(&self.code); + if !PathBuf::from(&p).exists() { + fs::create_dir(&p)? + } + + Ok(p.to_string_lossy().to_string()) + } + + /// get scripts path + pub fn scripts(mut self) -> Result { + let root = &self.root()?; + if self.scripts.is_none() { + self.scripts = Some("scripts".into()); + } + + let p = PathBuf::from(root).join(&self.scripts.ok_or(Error::NoneError)?); + if !PathBuf::from(&p).exists() { + std::fs::create_dir(&p)? + } + + Ok(p.to_string_lossy().to_string()) + } +} diff --git a/src/err.rs b/src/err.rs index ffffe77..a2d82b0 100644 --- a/src/err.rs +++ b/src/err.rs @@ -1,6 +1,6 @@ //! Errors in leetcode-cli use crate::cmds::{Command, DataCommand}; -use crate::config::{root, DEFAULT_CONFIG}; +use crate::config::{Config, DEFAULT_CONFIG}; use colored::Colorize; use std::fmt; @@ -100,7 +100,7 @@ impl std::convert::From for Error { // toml impl std::convert::From for Error { fn from(_err: toml::de::Error) -> Self { - let conf = root().unwrap().join("leetcode_tmp.toml"); + let conf = Config::root().unwrap().join("leetcode_tmp.toml"); std::fs::write(&conf, &DEFAULT_CONFIG[1..]).unwrap(); #[cfg(debug_assertions)] let err_msg = format!( diff --git a/src/helper.rs b/src/helper.rs index 7a1b40b..1d64ed7 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -190,7 +190,7 @@ mod file { /// Generate test cases path by fid pub fn test_cases_path(problem: &Problem) -> Result { - let conf = crate::config::locate()?; + let conf = crate::config::Config::locate()?; let mut path = format!("{}/{}.tests.dat", conf.storage.code()?, conf.code.pick); path = path.replace("${fid}", &problem.fid.to_string()); @@ -200,7 +200,7 @@ mod file { /// Generate code path by fid pub fn code_path(problem: &Problem, l: Option) -> Result { - let conf = crate::config::locate()?; + let conf = crate::config::Config::locate()?; let mut lang = conf.code.lang; if l.is_some() { lang = l.ok_or(Error::NoneError)?; @@ -223,7 +223,7 @@ mod file { pub fn load_script(module: &str) -> Result { use std::fs::File; use std::io::Read; - let conf = crate::config::locate()?; + let conf = crate::config::Config::locate()?; let mut script = "".to_string(); File::open(format!("{}/{}.py", conf.storage.scripts()?, module))? .read_to_string(&mut script)?; diff --git a/src/plugins/chrome.rs b/src/plugins/chrome.rs index 1105f48..7e7f368 100644 --- a/src/plugins/chrome.rs +++ b/src/plugins/chrome.rs @@ -41,7 +41,7 @@ impl std::string::ToString for Ident { /// Get cookies from chrome storage pub fn cookies() -> Result { - let ccfg = crate::config::locate()?.cookies; + let ccfg = crate::config::Config::locate()?.cookies; if !ccfg.csrf.is_empty() && !ccfg.session.is_empty() { return Ok(Ident { csrf: ccfg.csrf, diff --git a/src/plugins/leetcode.rs b/src/plugins/leetcode.rs index 1e7b17f..51c3180 100644 --- a/src/plugins/leetcode.rs +++ b/src/plugins/leetcode.rs @@ -36,7 +36,7 @@ impl LeetCode { /// New LeetCode client pub fn new() -> Result { - let conf = config::locate()?; + let conf = config::Config::locate()?; let cookies = chrome::cookies()?; let default_headers = LeetCode::headers( HeaderMap::new(), From ea9af32de55710a93cb3d3a65e6ba2b4416546cb Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 14 Aug 2022 21:30:18 +0800 Subject: [PATCH 04/12] feat(config): add default values for config --- src/config/code.rs | 18 ++++++++++++++++++ src/config/cookies.rs | 2 +- src/config/mod.rs | 28 ++-------------------------- src/config/storage.rs | 11 +++++++++++ src/err.rs | 3 --- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/config/code.rs b/src/config/code.rs index 1f8f49e..d7c583d 100644 --- a/src/config/code.rs +++ b/src/config/code.rs @@ -17,3 +17,21 @@ pub struct Code { pub pick: String, pub submission: String, } + +impl Default for Code { + fn default() -> Self { + Self { + editor: "vim".into(), + editor_args: None, + edit_code_marker: false, + start_marker: "".into(), + end_marker: "".into(), + comment_problem_desc: false, + comment_leading: "".into(), + test: true, + lang: "rust".into(), + pick: "${fid}.${slug}".into(), + submission: "${fid}.${slug}.${sid}.${ac}".into(), + } + } +} diff --git a/src/config/cookies.rs b/src/config/cookies.rs index 0c75a90..34dbda8 100644 --- a/src/config/cookies.rs +++ b/src/config/cookies.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; /// Cookies settings -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Default, Clone, Debug, Deserialize, Serialize)] pub struct Cookies { pub csrf: String, pub session: String, diff --git a/src/config/mod.rs b/src/config/mod.rs index 4eb60f9..808d6be 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -17,32 +17,8 @@ mod cookies; mod storage; mod sys; -pub const DEFAULT_CONFIG: &str = r#" -[code] -editor = "vim" -lang = "rust" -edit_code_marker = false -comment_problem_desc = false -comment_leading = "///" -start_marker = "@lc code=start" -end_marker = "@lc code=start" -test = true -pick = "${fid}.${slug}" -submission = "${fid}.${slug}.${sid}.${ac}" - -[cookies] -csrf = "" -session = "" - -[storage] -root = "~/.leetcode" -scripts = "scripts" -code = "code" -cache = "Problems" -"#; - /// Sync with `~/.leetcode/config.toml` -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Config { #[serde(skip)] pub sys: Sys, @@ -56,7 +32,7 @@ impl Config { pub fn locate() -> Result { let conf = Self::root()?.join("leetcode.toml"); if !conf.is_file() { - fs::write(&conf, &DEFAULT_CONFIG.trim())?; + fs::write(&conf, toml::ser::to_string_pretty(&Self::default())?)?; } let s = fs::read_to_string(&conf)?; diff --git a/src/config/storage.rs b/src/config/storage.rs index 6a0284e..301aca7 100644 --- a/src/config/storage.rs +++ b/src/config/storage.rs @@ -14,6 +14,17 @@ pub struct Storage { scripts: Option, } +impl Default for Storage { + fn default() -> Self { + Self { + cache: "Problems".into(), + code: "code".into(), + scripts: Some("scripts".into()), + root: "~/.leetcode".into(), + } + } +} + impl Storage { /// convert root path pub fn root(&self) -> Result { diff --git a/src/err.rs b/src/err.rs index a2d82b0..f5bfff6 100644 --- a/src/err.rs +++ b/src/err.rs @@ -1,6 +1,5 @@ //! Errors in leetcode-cli use crate::cmds::{Command, DataCommand}; -use crate::config::{Config, DEFAULT_CONFIG}; use colored::Colorize; use std::fmt; @@ -100,8 +99,6 @@ impl std::convert::From for Error { // toml impl std::convert::From for Error { fn from(_err: toml::de::Error) -> Self { - let conf = Config::root().unwrap().join("leetcode_tmp.toml"); - std::fs::write(&conf, &DEFAULT_CONFIG[1..]).unwrap(); #[cfg(debug_assertions)] let err_msg = format!( "{}, {}{}{}{}{}{}", From 7d055a5df1f7940f9685f9d99c68720b3f40c57e Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 06:51:08 +0800 Subject: [PATCH 05/12] feat(config): make config more simple --- src/cfg.rs | 241 --------------------------------------------- src/config/code.rs | 9 +- src/config/mod.rs | 24 ++++- src/config/sys.rs | 6 +- src/err.rs | 2 +- 5 files changed, 32 insertions(+), 250 deletions(-) delete mode 100644 src/cfg.rs diff --git a/src/cfg.rs b/src/cfg.rs deleted file mode 100644 index de152a5..0000000 --- a/src/cfg.rs +++ /dev/null @@ -1,241 +0,0 @@ -//! Soft-link with `config.toml` -//! -//! leetcode-cli will generate a `leetcode.toml` by default, -//! if you wanna change to it, you can: -//! -//! + Edit leetcode.toml at `~/.leetcode/leetcode.toml` directly -//! + Use `leetcode config` to update it -use crate::Error; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs, path::PathBuf}; - -pub const DEFAULT_CONFIG: &str = r##" -# usually you don't wanna change those -[sys] -categories = [ - "algorithms", - "concurrency", - "database", - "shell" -] - -langs = [ - "bash", - "c", - "cpp", - "csharp", - "elixir", - "golang", - "java", - "javascript", - "kotlin", - "mysql", - "php", - "python", - "python3", - "ruby", - "rust", - "scala", - "swift", - "typescript" -] - -[sys.urls] -base = "https://leetcode.com" -graphql = "https://leetcode.com/graphql" -login = "https://leetcode.com/accounts/login/" -problems = "https://leetcode.com/api/problems/$category/" -problem = "https://leetcode.com/problems/$slug/description/" -tag = "https://leetcode.com/tag/$slug/" -test = "https://leetcode.com/problems/$slug/interpret_solution/" -session = "https://leetcode.com/session/" -submit = "https://leetcode.com/problems/$slug/submit/" -submissions = "https://leetcode.com/api/submissions/$slug" -submission = "https://leetcode.com/submissions/detail/$id/" -verify = "https://leetcode.com/submissions/detail/$id/check/" -favorites = "https://leetcode.com/list/api/questions" -favorite_delete = "https://leetcode.com/list/api/questions/$hash/$id" - -[code] -editor = "vim" -lang = "rust" -edit_code_marker = false -comment_problem_desc = false -comment_leading = "///" -start_marker = "@lc code=start" -end_marker = "@lc code=start" -test = true -pick = "${fid}.${slug}" -submission = "${fid}.${slug}.${sid}.${ac}" - -[cookies] -csrf = "" -session = "" - -[storage] -root = "~/.leetcode" -scripts = "scripts" -code = "code" -# absolutely path for the cache, other use root as parent dir -cache = "~/.cache/leetcode" -"##; - -/// Sync with `~/.leetcode/config.toml` -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Config { - pub sys: Sys, - pub code: Code, - pub cookies: Cookies, - pub storage: Storage, -} - -impl Config { - /// Sync new config to config.toml - pub fn sync(&self) -> Result<(), Error> { - let home = dirs::home_dir().ok_or(Error::NoneError)?; - let conf = home.join(".leetcode/leetcode.toml"); - fs::write(conf, toml::ser::to_string_pretty(&self)?)?; - - Ok(()) - } -} - -/// Cookie settings -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Cookies { - pub csrf: String, - pub session: String, -} - -/// System settings, for leetcode api mainly -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Sys { - pub categories: Vec, - pub langs: Vec, - pub urls: HashMap, -} - -/// Leetcode API -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Urls { - pub base: String, - pub graphql: String, - pub login: String, - pub problems: String, - pub problem: String, - pub test: String, - pub session: String, - pub submit: String, - pub submissions: String, - pub submission: String, - pub verify: String, - pub favorites: String, - pub favorite_delete: String, -} - -/// default editor and langs -/// -/// + support editor: [emacs, vim] -/// + support langs: all in config -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Code { - pub editor: String, - #[serde(rename(serialize = "editor_args", deserialize = "editor_args"))] - pub editor_args: Option>, - pub edit_code_marker: bool, - pub start_marker: String, - pub end_marker: String, - pub comment_problem_desc: bool, - pub comment_leading: String, - pub test: bool, - pub lang: String, - pub pick: String, - pub submission: String, -} - -/// Locate code files -/// -/// + cache -> the path to cache -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Storage { - cache: String, - code: String, - root: String, - scripts: Option, -} - -impl Storage { - /// convert root path - pub fn root(&self) -> Result { - let home = dirs::home_dir() - .ok_or(Error::NoneError)? - .to_string_lossy() - .to_string(); - let path = self.root.replace('~', &home); - Ok(path) - } - - /// get cache path - pub fn cache(&self) -> Result { - let home = dirs::home_dir() - .ok_or(Error::NoneError)? - .to_string_lossy() - .to_string(); - let path = PathBuf::from(self.cache.replace('~', &home)); - if !path.is_dir() { - info!("Generate cache dir at {:?}.", &path); - fs::DirBuilder::new().recursive(true).create(&path)?; - } - - Ok(path.join("Problems").to_string_lossy().to_string()) - } - - /// get code path - pub fn code(&self) -> Result { - let root = &self.root()?; - let p = PathBuf::from(root).join(&self.code); - if !PathBuf::from(&p).exists() { - fs::create_dir(&p)? - } - - Ok(p.to_string_lossy().to_string()) - } - - /// get scripts path - pub fn scripts(mut self) -> Result { - let root = &self.root()?; - if self.scripts.is_none() { - let tmp = toml::from_str::(DEFAULT_CONFIG)?; - self.scripts = Some(tmp.storage.scripts.ok_or(Error::NoneError)?); - } - - let p = PathBuf::from(root).join(self.scripts.ok_or(Error::NoneError)?); - if !PathBuf::from(&p).exists() { - std::fs::create_dir(&p)? - } - - Ok(p.to_string_lossy().to_string()) - } -} - -/// Locate lc's config file -pub fn locate() -> Result { - let conf = root()?.join("leetcode.toml"); - if !conf.is_file() { - fs::write(&conf, &DEFAULT_CONFIG[1..])?; - } - - let s = fs::read_to_string(&conf)?; - Ok(toml::from_str::(&s)?) -} - -/// Get root path of leetcode-cli -pub fn root() -> Result { - let dir = dirs::home_dir().ok_or(Error::NoneError)?.join(".leetcode"); - if !dir.is_dir() { - info!("Generate root dir at {:?}.", &dir); - fs::DirBuilder::new().recursive(true).create(&dir)?; - } - - Ok(dir) -} diff --git a/src/config/code.rs b/src/config/code.rs index d7c583d..9c7a7a0 100644 --- a/src/config/code.rs +++ b/src/config/code.rs @@ -4,14 +4,21 @@ use serde::{Deserialize, Serialize}; /// Code config #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Code { + #[serde(default)] pub editor: String, - #[serde(rename(serialize = "editor-args", deserialize = "editor-args"))] + #[serde(rename(serialize = "editor-args"), alias = "editor-args", default)] pub editor_args: Option>, + #[serde(default, skip_serializing)] pub edit_code_marker: bool, + #[serde(default, skip_serializing)] pub start_marker: String, + #[serde(default, skip_serializing)] pub end_marker: String, + #[serde(default, skip_serializing)] pub comment_problem_desc: bool, + #[serde(default, skip_serializing)] pub comment_leading: String, + #[serde(default, skip_serializing)] pub test: bool, pub lang: String, pub pick: String, diff --git a/src/config/mod.rs b/src/config/mod.rs index 808d6be..d2e7d1d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -10,17 +10,17 @@ use crate::{ Error, }; use serde::{Deserialize, Serialize}; -use std::fs; +use std::{fs, path::Path}; mod code; mod cookies; mod storage; mod sys; -/// Sync with `~/.leetcode/config.toml` +/// Sync with `~/.leetcode/leetcode.toml` #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Config { - #[serde(skip)] + #[serde(default, skip_serializing)] pub sys: Sys, pub code: Code, pub cookies: Cookies, @@ -28,15 +28,29 @@ pub struct Config { } impl Config { + fn write_default(p: impl AsRef) -> Result<(), crate::Error> { + fs::write(p.as_ref(), toml::ser::to_string_pretty(&Self::default())?)?; + + Ok(()) + } + /// Locate lc's config file pub fn locate() -> Result { let conf = Self::root()?.join("leetcode.toml"); + if !conf.is_file() { - fs::write(&conf, toml::ser::to_string_pretty(&Self::default())?)?; + Self::write_default(&conf)?; } let s = fs::read_to_string(&conf)?; - Ok(toml::from_str::(&s)?) + match toml::from_str::(&s) { + Ok(config) => Ok(config), + Err(e) => { + let tmp = Self::root()?.join("leetcode.tmp.toml"); + Self::write_default(tmp)?; + Err(e.into()) + } + } } /// Get root path of leetcode-cli diff --git a/src/config/sys.rs b/src/config/sys.rs index 4386bd9..82cfa4c 100644 --- a/src/config/sys.rs +++ b/src/config/sys.rs @@ -2,10 +2,12 @@ //! //! This section is a set of constants after #88 +use serde::{Deserialize, Serialize}; + const CATEGORIES: [&str; 4] = ["algorithms", "concurrency", "database", "shell"]; /// Leetcode API -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Urls { pub base: String, pub graphql: String, @@ -77,7 +79,7 @@ impl Urls { } /// System settings, for leetcode api mainly -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Sys { pub categories: Vec, pub urls: Urls, diff --git a/src/err.rs b/src/err.rs index 9edfe93..3e96a37 100644 --- a/src/err.rs +++ b/src/err.rs @@ -108,7 +108,7 @@ impl std::convert::From for Error { _err, "Parse config file failed, ", "leetcode-cli has just generated a new leetcode.toml at ", - "~/.leetcode/leetcode_tmp.toml,".green().bold().underline(), + "~/.leetcode/leetcode.tmp.toml,".green().bold().underline(), " the current one at ", "~/.leetcode/leetcode.toml".yellow().bold().underline(), " seems missing some keys, Please compare the new file and add the missing keys.\n", From c61e620125eb4b6242f5a885ee5880c36479a3c6 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 07:04:47 +0800 Subject: [PATCH 06/12] docs(README): add telegram link --- Cargo.toml | 4 ++-- README.md | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce78e55..b9010c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ path = "src/bin/lc.rs" [package] name = "leetcode-cli" -version = "0.3.13" -authors = ["clearloop "] +version = "0.4.0" +authors = ["clearloop "] edition = "2021" description = "Leet your code in command-line." repository = "https://github.com/clearloop/leetcode-cli" diff --git a/README.md b/README.md index 25d46a3..c5f8cab 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ # leetcode-cli -![Rust](https://github.com/clearloop/leetcode-cli/workflows/Rust/badge.svg) + +![Rust](https://github.com/clearloop/leetcode-cli/workflows/leetcode-cli/badge.svg) [![crate](https://img.shields.io/crates/v/leetcode-cli.svg)](https://crates.io/crates/leetcode-cli) [![doc](https://img.shields.io/badge/current-docs-brightgreen.svg)](https://docs.rs/leetcode-cli/) [![downloads](https://img.shields.io/crates/d/leetcode-cli.svg)](https://crates.io/crates/leetcode-cli) -[![gitter](https://img.shields.io/gitter/room/odditypark/leetcode-cli)](https://gitter.im/Odditypark/leetcode-cli) +[![telegram](https://img.shields.io/badge/telegram-blue?logo=telegram)](https://t.me/+U_5si6PhWykxZTI1) [![LICENSE](https://img.shields.io/crates/l/leetcode-cli.svg)](https://choosealicense.com/licenses/mit/) ## Installing ```sh # Required dependencies: -# +# # gcc # libssl-dev # libdbus-1-dev @@ -48,14 +49,14 @@ SUBCOMMANDS: ## Example -For example, given this config (can be found in `~/.leetcode/leetcode.toml`, it can be generated automatically with command: `leetcode list` if you are a new user): +For example, given this config (could be found at `~/.leetcode/leetcode.toml`): ```toml [code] lang = "rust" editor = "emacs" # Optional parameter -editor_args = ['-nw'] +editor-args = ['-nw'] ``` #### 1. pick @@ -144,7 +145,8 @@ leetcode exec 1 ## Cookies -The cookie plugin of leetcode-cli can work on OSX and [Linux][#1]. **If you are on a different platform, there are problems with caching the cookies**, you can manually input your LeetCode Cookies to the configuration file. +The cookie plugin of leetcode-cli can work on OSX and [Linux][#1]. **If you are on a different platform, there are problems with caching the cookies**, +you can manually input your LeetCode Cookies to the configuration file. ```toml [cookies] @@ -154,7 +156,6 @@ session = "..." For Example, using Chrome (after logging in to LeetCode): - #### Step 1 Open Chrome and navigate to the link below: @@ -166,6 +167,7 @@ chrome://settings/cookies/detail?site=leetcode.com #### Step 2 Copy `Content` from `LEETCODE_SESSION` and `csrftoken` to `session` and `csrf` in your configuration file, respectively: + ```toml [cookies] csrf = "${csrftoken}" @@ -189,13 +191,13 @@ import json; def plan(sps, stags): ## - # `print` in python is supported, - # if you want to know the data structures of these two args, + # `print` in python is supported, + # if you want to know the data structures of these two args, # just print them ## problems = json.loads(sps) tags = json.loads(stags) - + ret = [] tm = {} for tag in tags: @@ -215,17 +217,15 @@ Then run `list` with the filter that you just wrote: leetcode list -p plan1 ``` -And that's it! Enjoy! - +That's it! Enjoy! -## PR +## Contributions -[PRs][pr] are more than welcome! +Feel free to add your names and emails in the `authors` field of `Cargo.toml` ! ## LICENSE MIT - [pr]: https://github.com/clearloop/leetcode-cli/pulls [#1]: https://github.com/clearloop/leetcode-cli/issues/1 From 87110b2cf070be08db8cff0f9ac9d8c89a17edfb Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 07:06:52 +0800 Subject: [PATCH 07/12] chore(lock): udpate cargo.lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04b9ebb..ebff3cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,18 +232,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.6" +version = "4.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6320c6d1c98b6981da7bb2dcecbd0be9dc98d42165fa8326b21000f7dbfde6d0" +checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.5" +version = "4.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" +checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" dependencies = [ "anstream", "anstyle", @@ -1025,7 +1025,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "leetcode-cli" -version = "0.3.13" +version = "0.4.0" dependencies = [ "async-trait", "cargo-husky", From bd8fd18b5ca9cdc1a4ccc3adcf8c9b0d7467bba2 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 07:34:49 +0800 Subject: [PATCH 08/12] fix(config): path of cache --- src/config/storage.rs | 14 +++++--------- src/config/sys.rs | 9 ++++++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/config/storage.rs b/src/config/storage.rs index 301aca7..72a01fc 100644 --- a/src/config/storage.rs +++ b/src/config/storage.rs @@ -38,17 +38,13 @@ impl Storage { /// get cache path pub fn cache(&self) -> Result { - let home = dirs::home_dir() - .ok_or(Error::NoneError)? - .to_string_lossy() - .to_string(); - let path = PathBuf::from(self.cache.replace('~', &home)); - if !path.is_dir() { - info!("Generate cache dir at {:?}.", &path); - fs::DirBuilder::new().recursive(true).create(&path)?; + let root = PathBuf::from(self.root()?); + if !root.exists() { + info!("Generate cache dir at {:?}.", &root); + fs::DirBuilder::new().recursive(true).create(&root)?; } - Ok(path.join("Problems").to_string_lossy().to_string()) + Ok(root.join("Problems").to_string_lossy().to_string()) } /// get code path diff --git a/src/config/sys.rs b/src/config/sys.rs index 82cfa4c..7c6f77e 100644 --- a/src/config/sys.rs +++ b/src/config/sys.rs @@ -6,6 +6,11 @@ use serde::{Deserialize, Serialize}; const CATEGORIES: [&str; 4] = ["algorithms", "concurrency", "database", "shell"]; +// TODO: find a better solution. +fn categories() -> Vec { + CATEGORIES.into_iter().map(|s| s.into()).collect() +} + /// Leetcode API #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Urls { @@ -54,7 +59,7 @@ impl Urls { /// problems url with specific `$category` pub fn problems(&self, category: &str) -> String { - self.problem.replace("$category", category) + self.problems.replace("$category", category) } /// submit url with specific `$slug` @@ -81,7 +86,9 @@ impl Urls { /// System settings, for leetcode api mainly #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Sys { + #[serde(default = "categories")] pub categories: Vec, + #[serde(default)] pub urls: Urls, } From fae42dea0f6c074fec0361a1e036be06c32d8c89 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:48:35 +0800 Subject: [PATCH 09/12] chore(plugin): disable chrome plugin --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/cache/mod.rs | 12 +++++++++--- src/config/cookies.rs | 6 ++++++ src/err.rs | 11 +++++++++-- src/plugins/leetcode.rs | 9 ++------- src/plugins/mod.rs | 4 +++- 7 files changed, 37 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebff3cc..c2dc11d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,6 +78,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + [[package]] name = "async-compression" version = "0.4.0" @@ -1027,6 +1033,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "leetcode-cli" version = "0.4.0" dependencies = [ + "anyhow", "async-trait", "cargo-husky", "clap", diff --git a/Cargo.toml b/Cargo.toml index b9010c7..e5ac98f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ serde_json = "1.0.82" toml = "0.5.9" regex = "1.6.0" scraper = "0.13.0" +anyhow = "1.0.71" [dependencies.diesel] version = "2.0.3" diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 9b7bca9..b15f677 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -8,6 +8,7 @@ use self::schemas::{problems::dsl::*, tags::dsl::*}; use self::sql::*; use crate::helper::test_cases_path; use crate::{config::Config, err::Error, plugins::LeetCode}; +use anyhow::anyhow; use colored::Colorize; use diesel::prelude::*; use reqwest::Response; @@ -342,15 +343,20 @@ impl Cache { ) -> Result { trace!("Exec problem filter —— Test or Submit"); let (json, [url, refer]) = self.pre_run_code(run.clone(), rfid, test_case).await?; - trace!("Pre run code result {:?}, {:?}, {:?}", json, url, refer); + trace!("Pre run code result {:#?}, {}, {}", json, url, refer); - let run_res: RunCode = self + let text = self .0 .clone() .run_code(json.clone(), url.clone(), refer.clone()) .await? - .json() // does not require LEETCODE_SESSION (very oddly) + .text() .await?; + + let run_res: RunCode = serde_json::from_str(&text).map_err(|e| { + anyhow!("json error: {e}, plz double check your session and csrf config.") + })?; + trace!("Run code result {:#?}", run_res); // Check if leetcode accepted the Run request diff --git a/src/config/cookies.rs b/src/config/cookies.rs index 34dbda8..d03cd4a 100644 --- a/src/config/cookies.rs +++ b/src/config/cookies.rs @@ -7,3 +7,9 @@ pub struct Cookies { pub csrf: String, pub session: String, } + +impl std::string::ToString for Cookies { + fn to_string(&self) -> String { + format!("LEETCODE_SESSION={};csrftoken={};", self.session, self.csrf) + } +} diff --git a/src/err.rs b/src/err.rs index 3e96a37..6421cfd 100644 --- a/src/err.rs +++ b/src/err.rs @@ -5,7 +5,6 @@ use std::fmt; // fixme: use this_error /// Error enum -#[derive(Clone)] pub enum Error { MatchError, DownloadError(String), @@ -20,6 +19,7 @@ pub enum Error { SilentError, NoneError, ChromeNotLogin, + Anyhow(anyhow::Error), } impl std::fmt::Debug for Error { @@ -59,7 +59,8 @@ impl std::fmt::Debug for Error { "json from response parse failed, please open a new issue at: {}.", "https://github.com/clearloop/leetcode-cli/".underline(), ), - Error::ChromeNotLogin => write!(f, "maybe you not login on the Chrome, you can login and retry.") + Error::ChromeNotLogin => write!(f, "maybe you not login on the Chrome, you can login and retry."), + Error::Anyhow(e) => write!(f, "{} {}", e, e), } } } @@ -147,6 +148,12 @@ impl std::convert::From for Error { } } +impl From for Error { + fn from(err: anyhow::Error) -> Self { + Error::Anyhow(err) + } +} + // pyo3 #[cfg(feature = "pym")] impl std::convert::From for Error { diff --git a/src/plugins/leetcode.rs b/src/plugins/leetcode.rs index 51c3180..6463ff1 100644 --- a/src/plugins/leetcode.rs +++ b/src/plugins/leetcode.rs @@ -2,7 +2,7 @@ use self::req::{Json, Mode, Req}; use crate::{ config::{self, Config}, err::Error, - plugins::chrome, + // plugins::chrome, }; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, @@ -37,7 +37,7 @@ impl LeetCode { /// New LeetCode client pub fn new() -> Result { let conf = config::Config::locate()?; - let cookies = chrome::cookies()?; + let cookies = conf.cookies.clone(); let default_headers = LeetCode::headers( HeaderMap::new(), vec![ @@ -53,11 +53,6 @@ impl LeetCode { .connect_timeout(Duration::from_secs(30)) .build()?; - // Sync conf - if conf.cookies.csrf != cookies.csrf { - conf.sync()?; - } - Ok(LeetCode { conf, client, diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 5a84870..9b2d903 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -6,6 +6,8 @@ //! ## login to `leetcode.com` //! Leetcode-cli use chrome cookie directly, do not need to login, please make sure you have loggined in `leetcode.com` before usnig `leetcode-cli` //! -mod chrome; + +// FIXME: Read cookies from local storage. (issue #122) +// mod chrome; mod leetcode; pub use leetcode::LeetCode; From 7418a070808986ed4be34f4bcd404f66006fdf32 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:52:16 +0800 Subject: [PATCH 10/12] docs(README): update config example --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5f8cab..2dfb39b 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,22 @@ For example, given this config (could be found at `~/.leetcode/leetcode.toml`): ```toml [code] -lang = "rust" -editor = "emacs" +editor = emacs # Optional parameter editor-args = ['-nw'] +lang = 'rust' +pick = '${fid}.${slug}' +submission = '${fid}.${slug}.${sid}.${ac}' + +[cookies] +csrf = '' +session = '' + +[storage] +cache = 'Problems' +code = 'code' +root = '~/.leetcode' +scripts = 'scripts' ``` #### 1. pick From 05aeeaa773a755da7cd8b003d586bb932af1aba6 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:58:17 +0800 Subject: [PATCH 11/12] feat(config): hide submission and pick --- README.md | 2 -- src/config/code.rs | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2dfb39b..f254d92 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,6 @@ editor = emacs # Optional parameter editor-args = ['-nw'] lang = 'rust' -pick = '${fid}.${slug}' -submission = '${fid}.${slug}.${sid}.${ac}' [cookies] csrf = '' diff --git a/src/config/code.rs b/src/config/code.rs index 9c7a7a0..0384608 100644 --- a/src/config/code.rs +++ b/src/config/code.rs @@ -1,6 +1,14 @@ //! Code in config use serde::{Deserialize, Serialize}; +fn default_pick() -> String { + "${fid}.${slug}".into() +} + +fn default_submission() -> String { + "${fid}.${slug}.${sid}.${ac}".into() +} + /// Code config #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Code { @@ -21,7 +29,9 @@ pub struct Code { #[serde(default, skip_serializing)] pub test: bool, pub lang: String, + #[serde(default = "default_pick", skip_serializing)] pub pick: String, + #[serde(default = "default_submission", skip_serializing)] pub submission: String, } From 1535c1acc54d14d88723250617ef9b9314d52d6a Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:58:53 +0800 Subject: [PATCH 12/12] chore(husky): remove cargo-husky --- .cargo-husky/hooks/pre-commit | 11 --------- Cargo.lock | 43 +++++++++++++++++++++++------------ Cargo.toml | 5 ---- 3 files changed, 29 insertions(+), 30 deletions(-) delete mode 100755 .cargo-husky/hooks/pre-commit diff --git a/.cargo-husky/hooks/pre-commit b/.cargo-husky/hooks/pre-commit deleted file mode 100755 index f735ef4..0000000 --- a/.cargo-husky/hooks/pre-commit +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# -# An example hook script to verify what is about to be committed. -# Called by "git commit" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message if -# it wants to stop the commit. -# -# To enable this hook, rename this file to "pre-commit". - -cargo build -cargo test diff --git a/Cargo.lock b/Cargo.lock index c2dc11d..e75ea22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,12 +209,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "cargo-husky" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad" - [[package]] name = "cc" version = "1.0.79" @@ -526,6 +520,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -778,7 +778,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -791,6 +791,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -943,7 +949,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -1035,7 +1051,6 @@ version = "0.4.0" dependencies = [ "anyhow", "async-trait", - "cargo-husky", "clap", "colored", "diesel", @@ -2306,17 +2321,17 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index e5ac98f..fcaafac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,11 +42,6 @@ features = ["sqlite"] version = "0.11.11" features = ["gzip", "json"] -[dev-dependencies.cargo-husky] -version = "1.5.0" -default-features = false -features = ["precommit-hook", "user-hooks"] - [features] pym = ["pyo3"] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy