10000 feature:new script · bestgopher/leetcode@d9e20a5 · GitHub
[go: up one dir, main page]

Skip to content

Commit d9e20a5

Browse files
committed
feature:new script
1 parent e197726 commit d9e20a5

File tree

9 files changed

+401
-1
lines changed

9 files changed

+401
-1
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ git2 = "0.13.15"
1111
reqwest = { version = "0.10", features = ["blocking", "json"] }
1212
serde_json = "1.0"
1313
serde = { version = "1.0", features = ["derive"] }
14+
clap = "3.0.0-beta.2"
15+
tera = "1.12.1"
16+
lazy_static = "1.4.0"
17+
regex = "1"
18+
1419

1520
[[bin]]
1621
name ="leetcode"
17-
path ="src/main.rs"
22+
path = "src/main.rs"

src/all.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::file;
2+
3+
use std::sync::{Arc, Mutex};
4+
use std::thread;
5+
use crate::http::Resp;
6+
7+
8+
/// 重新格式化
9+
pub fn all() {
10+
let files = file::get_all_bin_file();
11+
12+
let mut v = Vec::<Resp>::with_capacity(files.len());
13+
14+
let x = Arc::new(Mutex::new(v));
15+
let mut handlers = vec![];
16+
17+
for i in 0..=files.len() / 10 {
18+
// 把files分块,分成10个文件一块
19+
let files = if i * 10 + 10 > files.len() {
20+
files[i * 10..files.len()].to_vec()
21+
} else {
22+
files[i * 10..i * 10 + 10].to_vec()
23+
};
24+
25+
let x = x.clone();
26+
27+
handlers.push(thread::spawn(move || {
28+
for i in files {
29+
let resp = crate::http::get_question_info(&i);
30+
x.lock().unwrap().push(resp);
31+
}
32+
}))
33+
}
34+
35+
for i in handlers {
36+
i.join();
37+
}
38+
39+
crate::file::write_readme(&mut *x.lock().unwrap());
40+
}

src/file.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use std::fs::{self, File};
2+
use std::io::Write;
3+
use lazy_static::lazy_static;
4+
use regex::Regex;
5+
6+
use crate::http::{Resp, Data, Ques};
7+
8+
lazy_static!(
9+
static ref RE: Regex = Regex::new(r"\|\s*([0-9]*)\s*\|\s*(\w*)\s*\|.*?bin/(.*?).rs.*?\|.*?\|").unwrap();
10+
);
11+
12+
/// 将结果写入README.md中
13+
pub fn write_readme(r: &mut Vec<Resp>) {
14+
// 先按id排序
15+
r.sort_by(|x, y| {
16+
let x_id = x.data.question.question_id.parse::<i32>().unwrap();
17+
let y_id = y.data.question.question_id.parse::<i32>().unwrap();
18+
x_id.cmp(&y_id)
19+
});
20+
let s = crate::render::render(r).unwrap();
21+
22+
match std::fs::write("README.md", s) {
23+
Ok(_) => (),
24+
Err(e) => println!("写入 README.md 失败,err{}", e.to_string())
25+
}
26+
}
27+
28+
/// 获取 src/bin 目录下所有文件的名称
29+
pub fn get_all_bin_file() -> Vec<String> {
30+
let dir = fs::read_dir("src/bin/").unwrap();
31+
dir.
32+
into_iter().
33+
map(|x| {
34+
x.unwrap().file_name().to_str().unwrap().trim_end_matches(".rs").to_string()
35+
}).
36+
collect()
37+
}
38+
39+
40+
/// 创建 bin/{quest_name}.rs 文件
41+
pub fn write_question(resp: Resp) {
42+
let file = format!("src/bin/{}.rs", resp.data.question.title_slug);
43+
if std::path::Path::new(file.as_str()).exists() {
44+
println!("{} exists", file);
45+
return;
46+
}
47+
48+
let mut f = File::create(file.as_str()).unwrap();
49+
let mut s = String::new();
50+
s.push_str("fn main() {}\n\nstruct Solution;\n\n");
51+
52+
for i in resp.data.question.code_snippets {
53+
if i.lang == "Rust" {
54+
s.push_str(i.code.replace("↵", "\n").as_str());
55+
s.push_str("\n");
56+
break;
57+
}
58+
}
59+
60+
f.write_all(s.as_bytes()).unwrap();
61+
}
62+
63+
/// 解析README.md
64+
pub fn parse_readme() -> Vec<Resp> {
65+
let contents = fs::read_to_string("README.md").unwrap();
66+
parse(&contents)
67+
}
68+
69+
fn parse(contents: &str) -> Vec<Resp> {
70+
let mut v = vec![];
71+
for content in contents.split('\n') {
72+
for i in RE.captures_iter(content.trim()) {
73+
v.push(Resp {
74+
data: Data {
75+
question: Ques {
76+
question_id: i.get(1).unwrap().as_str().to_string(),
77+
translated_title: i.get(2).unwrap().as_str().to_string(),
78+
title_slug: String::new(),
79+
code_snippets: vec![],
80+
difficulty: String::new()
81+
}
82+
},
83+
})
84+
}
85+
}
86+
87+
v
88+
}
89+
90+
#[cfg(test)]
91+
mod tests {
92+
use crate::file::{parse, get_all_bin_file};
93+
94+
#[test]
95+
fn test_parse_readme() {
96+
let content = r"| 1111 | 两数之和| [src](https://github.com/rustors/leetcode/blob/main/src/bin/two_sum.rs) | [leetcode](https://leetcode-cn.com/problems/two_sum/) |
97+
| 1112 | 两数之和| [src](https://github.com/rustors/leetcode/blob/main/src/bin/two_sum.rs) | [leetcode](https://leetcode-cn.com/problems/two_sum/) |
98+
";
99+
100+
println!("{:?}", parse(content));
101+
}
102+
103+
#[test]
104+
fn test_get_all_bin_file() {
105+
println!("{:?}", get_all_bin_file());
106+
}
107+
}

src/git.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use git2::{Repository, StatusOptions};
2+
3+
use std::process::Command;
4+
5+
/// 把新文件加入到git中
6+
pub fn push() {
7+
// 解析readme.md文件
8+
let mut r = crate::file::parse_readme();
9+
let new_file = get_uncommit_files();
10+
for i in new_file.iter() {
11+
let x = i.trim_end_matches(".rs"); // 去掉后缀
12+
let x = x.trim_start_matches("src/bin/"); // 去掉路径
13+
git_add(i);
14+
r.push(crate::http::get_question_info(x));
15+
}
16+
17+
crate::file::write_readme(&mut r);
18+
git_add("README.md");
19+
push_to_origin();
20+
}
21+
22+
23+
fn get_uncommit_files() -> Vec<String> {
24+
let mut options = StatusOptions::new();
25+
options.pathspec("src/bin");
26+
options.include_untracked(true);
27+
let repo = Repository::open(".").unwrap();
28+
let statuses = repo.statuses(Some(&mut options)).unwrap();
29+
30+
statuses
31+
.iter()
32+
.map(|x| String::from(x.path().unwrap()))
33+
.collect()
34+
}
35+
36+
fn git_add(file: &str) {
37+
Command::new("git").arg("add").arg(file).output().unwrap();
38+
let output = Command::new("git")
39+
.arg("commit")
40+
.arg("-m")
41+
.arg(file)
42+
.output()
43+
.unwrap();
44+
45+
println!("{}", String::from_utf8(output.stdout).unwrap());
46+
}
47+
48+
pub fn push_to_origin() {
49+
let output = Command::new("git").arg("push").output().unwrap();
50+
println!("{}", String::from_utf8(output.stdout).unwrap());
51+
}
52+
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use crate::git::get_uncommit_files;
57+
58+
#[test]
59+
fn test_get_uncommit_files() {
60+
println!("{:?}", get_uncommit_files());
61+
}
62+
}

src/http.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use lazy_static::lazy_static;
2+
use regex::Regex;
3+
use reqwest::blocking::Client;
4+
use serde::{Serialize, Deserialize};
5+
6+
7+
lazy_static! {
8+
static ref RE: Regex = Regex::new(r".*?/problems/(.*?)/").unwrap();
9+
}
10+
11+
const URL: &'static str = "https://leetcode-cn.com/graphql/";
12+
13+
#[derive(Deserialize, Serialize, Debug)]
14+
pub struct Ques {
15+
#[serde(rename = "questionId")]
16+
pub question_id: String,
17+
#[serde(rename = "titleSlug")]
18+
pub title_slug: String,
19+
#[serde(rename = "translatedTitle")]
20+
pub translated_title: String,
21+
#[serde(rename = "codeSnippets")]
22+
pub code_snippets: Vec<CodeSnippets>,
23+
#[serde(rename = "difficulty")]
24+
pub difficulty: String,
25+
}
26+
27+
#[derive(Deserialize, Serialize, Debug)]
28+
pub struct CodeSnippets {
29+
#[serde(rename = "code")]
30+
pub code: String,
31+
#[serde(rename = "lang")]
32+
pub lang: String,
33+
#[serde(rename = "langSlug")]
34+
pub lang_slug: String,
35+
#[serde(rename = "__typename")]
36+
pub typename: String,
37+
}
38+
39+
#[derive(Deserialize, Serialize, Debug)]
40+
pub struct Data {
41+
pub question: Ques,
42+
}
43+
44+
#[derive(Deserialize, Serialize, Debug)]
45+
pub struct Resp {
46+
pub data: Data,
47+
}
48+
49+
pub fn get_question_info(mut ques: &str) -> Resp {
50+
if ques.starts_with("http") {
51+
ques = RE.captures_iter(ques).next().unwrap().get(1).unwrap().as_str();
52+
}
53+
54+
let data_fmt = r#"{"operationName":"questionData","variables":{"titleSlug":"{}"},
55+
"query":"query questionData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n questionId\n questionFrontendId\n boundTopicId\n title\n titleSlug\n content\n translatedTitle\n translatedContent\n isPaidOnly\n difficulty\n likes\n dislikes\n isLiked\n similarQuestions\n contributors {\n username\n profileUrl\n avatarUrl\n __typename\n }\n langToValidPlayground\n topicTags {\n name\n slug\n translatedName\n __typename\n }\n companyTagStats\n codeSnippets {\n lang\n langSlug\n code\n __typename\n }\n stats\n hints\n solution {\n id\n canSeeDetail\n __typename\n }\n status\n sampleTestCase\n metaData\n judgerAvailable\n judgeType\n mysqlSchemas\n enableRunCode\n envInfo\n book {\n id\n bookName\n pressName\n source\n shortDescription\n fullDescription\n bookImgUrl\n pressImgUrl\n productUrl\n __typename\n }\n isSubscribed\n isDailyQuestion\n dailyRecordStatus\n editorType\n ugcQuestionId\n style\n __typename\n }\n}\n"}"#;
56+
let data = data_fmt.replace("{}", ques);
57+
58+
Client::new()
59+
.post(URL)
60+
.header("content-type", "application/json")
61+
.body(data)
62+
.send()
63+
.unwrap()
64+
.json::<Resp>()
65+
.unwrap()
66+
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use crate::http::get_question_info;
71+
72+
#[test]
73+
fn test_get_question_info() {
74+
println!("{:?}", get_question_info("container-with-most-water"));
75+
println!("{:?}", get_question_info("https://leetcode-cn.com/problems/container-with-most-water/"));
76+
}
77+
}

src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
mod new;
2+
mod http;
3+
mod file;
4+
mod git;
5+
mod render;
6+
mod all;
7+
8+
use clap::{App, Arg};
9+
10+
pub fn run() {
11+
let matches = App::new("leetcode")
12+
.version("0.0.1")
13+
.author("bestgopher <84328409@qq.com>")
14+
.about("a helper for leetcode")
15+
.subcommand(App::new("new")
16+
.about("get a new leetcode question")
17+
.arg(Arg::new("question_name")
18+
.about("The configuration file to use")
19+
.index(1)))
20+
.subcommand(App::new("all")
21+
.about("get all questions' info and rewrite README.md"))
22+
.get_matches();
23+
24+
if let Some(matches) = matches.subcommand_matches("new") {
25+
match matches.value_of_t::<String>("question_name") {
26+
Ok(x) => new::new(x),
27+
Err(_) => println!("please input the name of question")
28+
}
29+
} else if let Some(_) = matches.subcommand_matches("all") {
30+
all::all();
31+
} else {
32+
git::push();
33+
}
34+
}

src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use leetcode;
2+
3+
fn main() {
4+
leetcode::run()
5+
}

src/new.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/// 1.获取question name
2+
/// 2.如果name是url,就不需要拼装,不是就需要拼装
3+
/// 3.请求结构,获取数据
4+
/// 4.将数据写入bin/{question_name}.rs文件中
5+
pub fn new(ques: String) {
6+
let r = crate::http::get_question_info(ques.as_str());
7+
crate::file::write_question(r);
8+
}

0 commit comments

Comments
 (0)
0