Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ path = "src/bin/lc.rs"

[package]
name = "leetcode-cli"
version = "0.5.4"
version = "0.5.5"
authors = ["clearloop <tianyi.gc@gmail.com>"]
edition = "2024"
description = "Leetcode command-line interface in rust."
Expand Down
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Controls the editor that opens and the code that gets generated.

```toml
[code]
# Overridden by $VISUAL, then $EDITOR, if either is set
editor = 'vim'
# Extra arguments passed to the editor
editor_args = ['-nw']
Expand All @@ -23,7 +24,7 @@ test = true

| Key | Default | Description |
| --- | --- | --- |
| `editor` | `'vim'` | Command used to open the solution file. |
| `editor` | `'vim'` | Command used to open the solution file. `$VISUAL` wins over `$EDITOR`, and both win over this key. |
| `editor_args` | — | Extra arguments passed before the file path. |
| `editor_envs` | — | Environment variables for the editor process, each as `"NAME=VALUE"`. |
| `lang` | `'rust'` | Language of the generated file. `leetcode edit --lang <lang>` overrides this per-call and persists it. |
Expand Down
10 changes: 5 additions & 5 deletions src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ impl Cache {

println!(
"\n[{}] {} {}\n\n",
&ids,
&target.name.bold().underline(),
ids,
target.name.bold().underline(),
"is on the run...".dimmed()
);

Expand All @@ -178,7 +178,7 @@ impl Cache {
.await?
.json()
.await?;
debug!("{:#?}", &json);
debug!("{:#?}", json);
match parser::desc(&mut rdesc, json) {
None => return Err(Error::NoneError),
Some(false) => {
Expand All @@ -202,13 +202,13 @@ impl Cache {
}

pub async fn get_tagged_questions(self, rslug: &str) -> Result<Vec<String>, Error> {
trace!("Geting {} questions...", &rslug);
trace!("Geting {} questions...", rslug);
let ids: Vec<String>;
let rtag = tags
.filter(tag.eq(rslug.to_string()))
.first::<Tag>(&mut self.conn()?);
if let Ok(t) = rtag {
trace!("Got {} questions from local cache...", &rslug);
trace!("Got {} questions from local cache...", rslug);
ids = serde_json::from_str(&t.refs)?;
} else {
ids = parser::tags(
Expand Down
80 changes: 39 additions & 41 deletions src/cache/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ impl std::fmt::Display for VerifyResult {
_ => self.expected.expected_code_answer.join("↩ "),
};

debug!("{:#?}", &self);
debug!("{:#?}", self);

match &self.status.status_code {
10 => {
Expand All @@ -311,14 +311,14 @@ impl std::fmt::Display for VerifyResult {
write!(
f,
"\n{}{}{}\n{}{}{}{}{}{}\n",
&self.status.status_msg.green().bold(),
&"Runtime: ".before_spaces(7).dimmed(),
&self.status.status_runtime.dimmed(),
&"\nYour input:".after_spaces(4),
&self.data_input.replace('\n', "↩ "),
&"\nOutput:".after_spaces(8),
self.status.status_msg.green().bold(),
"Runtime: ".before_spaces(7).dimmed(),
self.status.status_runtime.dimmed(),
"\nYour input:".after_spaces(4),
self.data_input.replace('\n', "↩ "),
"\nOutput:".after_spaces(8),
ca,
&"\nExpected:".after_spaces(6),
"\nExpected:".after_spaces(6),
eca,
)?
} else if matches!(self.result_type, Run::Submit)
Expand Down Expand Up @@ -375,17 +375,17 @@ impl std::fmt::Display for VerifyResult {
{} {}.\n\n",
"Success\n\n".green().bold(),
"Runtime: ".dimmed(),
&self.status.status_runtime.bold(),
self.status.status_runtime.bold(),
rp.to_string().bold(),
"% ".bold(),
&self.pretty_lang,
&self.name,
self.pretty_lang,
self.name,
"Memory Usage: ".dimmed(),
&self.status.status_memory.bold(),
self.status.status_memory.bold(),
mp.to_string().bold(),
"% ".bold(),
&self.pretty_lang,
&self.name,
self.pretty_lang,
self.name,
)?
} else {
// Wrong Answer during testing
Expand All @@ -394,12 +394,12 @@ impl std::fmt::Display for VerifyResult {
"\n{}{}{}\n{}{}{}{}{}{}\n",
"Wrong Answer".red().bold(),
" Runtime: ".dimmed(),
&self.status.status_runtime.dimmed(),
&"\nYour input:".after_spaces(4),
&self.data_input.replace('\n', "↩ "),
&"\nOutput:".after_spaces(8),
self.status.status_runtime.dimmed(),
"\nYour input:".after_spaces(4),
self.data_input.replace('\n', "↩ "),
"\nOutput:".after_spaces(8),
ca,
&"\nExpected:".after_spaces(6),
"\nExpected:".after_spaces(6),
eca,
)?
}
Expand All @@ -408,57 +408,55 @@ impl std::fmt::Display for VerifyResult {
11 => write!(
f,
"\n{}\n\n{}{}\n{}{}\n{}{}{}{}{}{}\n",
&self.status.status_msg.red().bold(),
self.status.status_msg.red().bold(),
"Cases passed:".after_spaces(2).green(),
&self
.analyse
self.analyse
.total_correct
.as_ref()
.unwrap_or(&Number::from(0))
.to_string()
.green(),
&"Total cases:".after_spaces(3).yellow(),
&self
.analyse
"Total cases:".after_spaces(3).yellow(),
self.analyse
.total_testcases
.as_ref()
.unwrap_or(&Number::from(0))
.to_string()
.bold()
.yellow(),
&"Last case:".after_spaces(5).dimmed(),
&self.submit.last_testcase.replace('\n', "↩ ").dimmed(),
&"\nOutput:".after_spaces(8),
"Last case:".after_spaces(5).dimmed(),
self.submit.last_testcase.replace('\n', "↩ ").dimmed(),
"\nOutput:".after_spaces(8),
self.code_output[0],
&"\nExpected:".after_spaces(6),
"\nExpected:".after_spaces(6),
self.expected_output[0],
)?,
// Memory Exceeded
12 => write!(
f,
"\n{}\n\n{}{}\n",
&self.status.status_msg.yellow().bold(),
&"Last case:".after_spaces(5).dimmed(),
&self.data_input.replace('\n', "↩ "),
self.status.status_msg.yellow().bold(),
"Last case:".after_spaces(5).dimmed(),
self.data_input.replace('\n', "↩ "),
)?,
// Output Timeout Exceeded
//
// TODO: 13 and 14 might have some different,
// if anybody reach this, welcome to fix this!
13 | 14 => write!(f, "\n{}\n", &self.status.status_msg.yellow().bold(),)?,
13 | 14 => write!(f, "\n{}\n", self.status.status_msg.yellow().bold(),)?,
// Runtime error
15 => write!(
f,
"\n{}\n{}\n'",
&self.status.status_msg.red().bold(),
&self.status.runtime_error
self.status.status_msg.red().bold(),
self.status.runtime_error
)?,
// Compile Error
20 => write!(
f,
"\n{}:\n\n{}\n",
&self.status.status_msg.red().bold(),
&self.error.full_compile_error.dimmed()
self.status.status_msg.red().bold(),
self.error.full_compile_error.dimmed()
)?,
_ => write!(
f,
Expand All @@ -482,8 +480,8 @@ impl std::fmt::Display for VerifyResult {
write!(
f,
"{}{}",
&"Stdout:".after_spaces(8).purple(),
&self.code_output.join(&"\n".after_spaces(15))
"Stdout:".after_spaces(8).purple(),
self.code_output.join(&"\n".after_spaces(15))
)
} else {
write!(f, "")
Expand All @@ -494,8 +492,8 @@ impl std::fmt::Display for VerifyResult {
write!(
f,
"{}{}",
&"Stdout:".after_spaces(8).purple(),
&self.std_output[0].replace('\n', &"\n".after_spaces(15))
"Stdout:".after_spaces(8).purple(),
self.std_output[0].replace('\n', &"\n".after_spaces(15))
)
} else {
write!(f, "")
Expand Down
22 changes: 20 additions & 2 deletions src/config/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ fn is_default_bool(t: &bool) -> bool {
!t
}

fn default_editor() -> String {
"vim".into()
}
Comment thread
astropanic marked this conversation as resolved.

/// Code config
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Code {
#[serde(default)]
#[serde(default = "default_editor")]
pub editor: String,
#[serde(rename(serialize = "editor-args"), alias = "editor-args", default)]
pub editor_args: Option<Vec<String>>,
Expand All @@ -47,10 +51,24 @@ pub struct Code {
pub pick: String,
}

impl Code {
/// `$VISUAL` and `$EDITOR` take precedence over the configured editor.
pub fn with_env_override(mut self) -> Self {
for key in ["EDITOR", "VISUAL"] {
if let Ok(editor) = std::env::var(key)
&& !editor.is_empty()
{
self.editor = editor;
}
}
self
}
}
Comment thread
clearloop marked this conversation as resolved.

impl Default for Code {
Comment thread
astropanic marked this conversation as resolved.
fn default() -> Self {
Self {
editor: "vim".into(),
editor: default_editor(),
editor_args: None,
editor_envs: None,
edit_code_marker: false,
Expand Down
48 changes: 25 additions & 23 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
config::{code::Code, cookies::Cookies, storage::Storage, sys::Sys},
};
use serde::{Deserialize, Serialize};
use std::{fs, path::Path};
use std::{fs, path::Path, str::FromStr};

mod code;
mod cookies;
Expand Down Expand Up @@ -44,34 +44,18 @@ impl Config {
Self::write_default(&conf)?;
}

let s = fs::read_to_string(&conf)?;
match toml::from_str::<Config>(&s) {
Ok(mut config) => {
// Override config.cookies with environment variables
config.cookies = config.cookies.with_env_override();

match config.cookies.site {
cookies::LeetcodeSite::LeetcodeCom => Ok(config),
cookies::LeetcodeSite::LeetcodeCn => {
let mut config = config;
config.sys.urls = sys::Urls::new_with_leetcode_cn();
Ok(config)
}
}
}
Err(e) => {
let tmp = Self::root()?.join("leetcode.tmp.toml");
Self::write_default(tmp)?;
Err(e.into())
}
}
fs::read_to_string(&conf)?
.parse::<Config>()
.inspect_err(|_| {
let _ = Self::write_default(conf.with_file_name("leetcode.tmp.toml"));
})
}

/// Get root path of leetcode-cli
pub fn root() -> Result<std::path::PathBuf> {
let dir = dirs::home_dir().ok_or(Error::NoneError)?.join(".leetcode");
if !dir.is_dir() {
info!("Generate root dir at {:?}.", &dir);
info!("Generate root dir at {:?}.", dir);
fs::DirBuilder::new().recursive(true).create(&dir)?;
}

Expand All @@ -87,3 +71,21 @@ impl Config {
Ok(())
}
}

impl FromStr for Config {
type Err = Error;

/// Parses `leetcode.toml`, applying the environment overrides on top of it.
fn from_str(s: &str) -> Result<Self> {
let mut config: Config = toml::from_str(s)?;

config.code = config.code.with_env_override();
config.cookies = config.cookies.with_env_override();

if let cookies::LeetcodeSite::LeetcodeCn = config.cookies.site {
config.sys.urls = sys::Urls::new_with_leetcode_cn();
}

Ok(config)
}
}
2 changes: 1 addition & 1 deletion src/config/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Storage {
pub fn cache(&self) -> Result<String> {
let root = PathBuf::from(self.root()?);
if !root.exists() {
info!("Generate cache dir at {:?}.", &root);
info!("Generate cache dir at {:?}.", root);
fs::DirBuilder::new().recursive(true).create(&root)?;
}

Expand Down
Loading