diff --git a/src/fetch_problem.rs b/src/fetch_problem.rs
new file mode 100644
index 0000000..6c3867a
--- /dev/null
+++ b/src/fetch_problem.rs
@@ -0,0 +1,92 @@
+use serde_derive::{Deserialize, Serialize};
+use serde_json::json;
+
+pub mod fetcher;
+pub mod manager;
+
+#[derive(Serialize, Deserialize)]
+pub struct CodeDefinition {
+ pub value: String,
+ pub text: String,
+ #[serde(rename = "defaultCode")]
+ pub default_code: String
+}
+
+/// LeetCode 单个问题
+pub struct Problem {
+ pub title: String,
+ pub title_slug: String,
+ pub content: String,
+ pub code_definition: Vec,
+ pub question_id: u32,
+ pub return_type: String
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Stat {
+ question_id: u32,
+ #[serde(rename = "question__article__slug")]
+ question_article_slug: Option,
+ #[serde(rename = "question__title")]
+ pub question_title: Option,
+ #[serde(rename = "question__title_slug")]
+ pub question_title_slug: Option,
+ #[serde(rename = "question__hide")]
+ question_hide: bool,
+ total_acs: u32,
+ total_submitted: u32,
+ pub frontend_question_id: String,
+ is_new_question: bool,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct StatWithStatus {
+ pub stat: Stat,
+ pub paid_only: bool,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Problems {
+ pub stat_status_pairs: Vec
+}
+
+const QUESTION_QUERY_STRING: &str = r#"
+query questionData($titleSlug: String!) {
+ question(titleSlug: $titleSlug) {
+ content
+ stats
+ codeDefinition
+ sampleTestCase
+ metaData
+ }
+}"#;
+const QUESTION_QUERY_OPERATION: &str = "questionData";
+
+/// 题目查询
+#[derive(Serialize, Deserialize)]
+pub struct Query {
+ #[serde(rename = "operationName")]
+ operation_name: String,
+ variables: serde_json::Value,
+ query: String
+}
+
+impl Query {
+ pub fn new(title: &str) -> Query {
+ Query {
+ operation_name: QUESTION_QUERY_OPERATION.to_owned(),
+ variables: json!({
+ "titleSlug": title
+ }),
+ query: QUESTION_QUERY_STRING.to_owned()
+ }
+ }
+}
+
+pub struct Fetcher {
+ client: reqwest::Client
+}
+
+pub struct ProblemManager {
+ pub problem_list: Vec,
+}
\ No newline at end of file
diff --git a/src/fetch_problem/fetcher.rs b/src/fetch_problem/fetcher.rs
new file mode 100644
index 0000000..5fc4c10
--- /dev/null
+++ b/src/fetch_problem/fetcher.rs
@@ -0,0 +1,104 @@
+use std::error::Error;
+use serde_derive::{Deserialize, Serialize};
+use super::{Problem, Problems, Query, Fetcher};
+
+const PROBLEMS_URL: &str = "https://leetcode.cn/api/problems/algorithms/";
+const GRAPHQL_URL: &str = "https://leetcode.cn/graphql";
+
+impl Fetcher {
+ pub fn new() -> Fetcher {
+ Fetcher {
+ client: reqwest::Client::new()
+ }
+ }
+
+ pub async fn get_problems(&self) -> Result {
+ Ok(reqwest::get(PROBLEMS_URL)
+ .await?
+ .json()
+ .await?
+ )
+ }
+
+ pub async fn get_problem(self, question_id: u32) -> Result> {
+ let problems = self.get_problems().await?;
+
+ for problem in &problems.stat_status_pairs {
+ match problem.stat.frontend_question_id.parse::() {
+ Ok(id) => {
+ if id == question_id {
+ if problem.paid_only {
+ return Err("failed to get paid only problem".into())
+ }
+
+ let query = match &problem.stat.question_title_slug {
+ None => {
+ Err::>("failed to get problem title slug".into())
+ }
+ Some(value) => {
+ Ok(Query::new(value.as_str()))
+ }
+ }?;
+
+ let response = self.client
+ .post(GRAPHQL_URL)
+ .json(&query)
+ .send()
+ .await?
+ .json::()
+ .await?;
+
+ let title = problem.stat.question_title.clone()
+ .ok_or::>("failed to get problem title".into())?;
+ let title_slug = problem.stat.question_title_slug.clone()
+ .ok_or::>("failed to get problem title slug".into())?;
+ let return_type = {
+ let v = serde_json::from_str::(
+ &response.data.question.meta_data);
+ v.and_then(|x| {
+ return Ok(x.to_string().replace("\"", ""))
+ })
+ }?;
+ let code_definition = serde_json::from_str(
+ &response.data.question.code_definition
+ )?;
+
+ return Ok(Problem {
+ title,
+ title_slug,
+ code_definition,
+ content: response.data.question.content,
+ question_id: id,
+ return_type
+ })
+ }
+ }
+ Err(_) => {}
+ }
+ }
+
+ Err("failed to get target problem".into())
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct RawProblem {
+ data: Data,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Data {
+ question: Question,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct Question {
+ content: String,
+ stats: String,
+ #[serde(rename = "codeDefinition")]
+ code_definition: String,
+ #[serde(rename = "sampleTestCase")]
+ sample_test_case: String,
+ #[serde(rename = "metaData")]
+ meta_data: String,
+}
\ No newline at end of file
diff --git a/src/fetch_problem/manager.rs b/src/fetch_problem/manager.rs
new file mode 100644
index 0000000..7a12977
--- /dev/null
+++ b/src/fetch_problem/manager.rs
@@ -0,0 +1,72 @@
+use super::{Problem, ProblemManager};
+use std::fs;
+use regex::{Regex};
+
+impl ProblemManager {
+ pub fn scan() -> Result> {
+ let pattern = Regex::new(r"p(\d{4})_")?;
+ let mut problems = Vec::new();
+ let mod_content = fs::read_to_string("./src/problem/mod.rs")?;
+
+ for i in pattern.captures_iter(&mod_content) {
+ match i.get(1) {
+ None => {}
+ Some(value) => {
+ match value.as_str().parse::() {
+ Ok(id) => {
+ problems.push(id);
+ }
+ Err(_) => {}
+ }
+ }
+ }
+ }
+
+ Ok(ProblemManager {
+ problem_list: problems
+ })
+ }
+}
+
+impl Problem {
+ pub fn get_filename(&self) -> String {
+ format!("p{}_{}", self.question_id, self.title_slug.replace('-', "_"))
+ }
+
+ pub fn get_file_content(&self) -> Result> {
+ let template = fs::read_to_string("./template.rs")?;
+
+ let code = self.code_definition
+ .iter()
+ .find(|x| x.value == "rust");
+
+ let code = code.ok_or::>(
+ format!("problem {} doesn't have rust version", self.question_id).into()
+ )?;
+
+ let source = template
+ .replace("__PROBLEM_TITLE__", &self.title)
+ .replace("__PROBLEM_ID__", self.question_id.to_string().as_str())
+ .replace(
+ "__PROBLEM_DEFAULT_CODE__",
+ &code.default_code)
+ .replace("__EXTRA_USE__", &parse_extra_use(&code.default_code));
+
+ Ok(source)
+ }
+}
+
+fn parse_extra_use(code: &str) -> String {
+ let mut extra_use_line = String::new();
+ // a linked-list problem
+ if code.contains("pub struct ListNode") {
+ extra_use_line.push_str("\nuse crate::util::linked_list::{ListNode, to_list};")
+ }
+ if code.contains("pub struct TreeNode") {
+ extra_use_line.push_str("\nuse crate::util::tree::{TreeNode, to_tree};")
+ }
+ if code.contains("pub struct Point") {
+ extra_use_line.push_str("\nuse crate::util::point::Point;")
+ }
+ extra_use_line
+}
\ No newline at end of file
diff --git a/src/fetcher.rs b/src/fetcher.rs
deleted file mode 100644
index f8cdd9d..0000000
--- a/src/fetcher.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-extern crate reqwest;
-extern crate serde_json;
-
-use serde_json::Value;
-use std::fmt::{Display, Error, Formatter};
-
-const PROBLEMS_URL: &str = "https://leetcode.cn/api/problems/algorithms/";
-const GRAPHQL_URL: &str = "https://leetcode.cn/graphql";
-const QUESTION_QUERY_STRING: &str = r#"
-query questionData($titleSlug: String!) {
- question(titleSlug: $titleSlug) {
- content
- stats
- codeDefinition
- sampleTestCase
- metaData
- }
-}"#;
-const QUESTION_QUERY_OPERATION: &str = "questionData";
-
-pub async fn get_problem(frontend_question_id: u32) -> Option {
- let problems = get_problems().await.unwrap();
- for problem in problems.stat_status_pairs.iter() {
- match problem.stat.frontend_question_id.parse::() {
- Ok(id) => {
- if id == frontend_question_id {
- if problem.paid_only {
- return None;
- }
-
- let client = reqwest::Client::new();
- let resp: RawProblem = client
- .post(GRAPHQL_URL)
- .json(&Query::question_query(
- problem.stat.question_title_slug.as_ref().unwrap(),
- ))
- .send()
- .await
- .unwrap()
- .json()
- .await
- .unwrap();
- return Some(Problem {
- title: problem.stat.question_title.clone().unwrap(),
- title_slug: problem.stat.question_title_slug.clone().unwrap(),
- code_definition: serde_json::from_str(&resp.data.question.code_definition).unwrap(),
- content: resp.data.question.content,
- sample_test_case: resp.data.question.sample_test_case,
- difficulty: problem.difficulty.to_string(),
- question_id: id,
- return_type: {
- let v: Value = serde_json::from_str(&resp.data.question.meta_data).unwrap();
- v["return"]["type"].to_string().replace("\"", "")
- },
- });
- }
- }
- _ => {}
- }
- }
- None
-}
-
-pub async fn get_problem_async(problem_stat: StatWithStatus) -> Option {
- if problem_stat.paid_only {
- println!(
- "Problem {} is paid-only",
- &problem_stat.stat.frontend_question_id
- );
- return None;
- }
- let resp = surf::post(GRAPHQL_URL).body_json(&Query::question_query(
- problem_stat.stat.question_title_slug.as_ref().unwrap(),
- ));
- if resp.is_err() {
- println!(
- "Problem {} not initialized due to some error",
- &problem_stat.stat.frontend_question_id
- );
- return None;
- }
- let resp = resp.unwrap().recv_json().await;
- if resp.is_err() {
- println!(
- "Problem {} not initialized due to some error",
- &problem_stat.stat.frontend_question_id
- );
- return None;
- }
- let resp: RawProblem = resp.unwrap();
- match problem_stat.stat.frontend_question_id.parse::() {
- Ok(id) => {
- return Some(Problem {
- title: problem_stat.stat.question_title.clone().unwrap(),
- title_slug: problem_stat.stat.question_title_slug.clone().unwrap(),
- code_definition: serde_json::from_str(&resp.data.question.code_definition).unwrap(),
- content: resp.data.question.content,
- sample_test_case: resp.data.question.sample_test_case,
- difficulty: problem_stat.difficulty.to_string(),
- question_id: id,
- return_type: {
- let v: Value = serde_json::from_str(&resp.data.question.meta_data).unwrap();
- v["return"]["type"].to_string().replace("\"", "")
- },
- });
- }
- _ => {
- None
- }
- }
-}
-
-pub async fn get_problems() -> Option {
- reqwest::get(PROBLEMS_URL).await.unwrap().json().await.unwrap()
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct Problem {
- pub title: String,
- pub title_slug: String,
- pub content: String,
- #[serde(rename = "codeDefinition")]
- pub code_definition: Vec,
- #[serde(rename = "sampleTestCase")]
- pub sample_test_case: String,
- pub difficulty: String,
- pub question_id: u32,
- pub return_type: String,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct CodeDefinition {
- pub value: String,
- pub text: String,
- #[serde(rename = "defaultCode")]
- pub default_code: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct Query {
- #[serde(rename = "operationName")]
- operation_name: String,
- variables: serde_json::Value,
- query: String,
-}
-
-impl Query {
- fn question_query(title_slug: &str) -> Query {
- Query {
- operation_name: QUESTION_QUERY_OPERATION.to_owned(),
- variables: json!({ "titleSlug": title_slug }),
- query: QUESTION_QUERY_STRING.to_owned(),
- }
- }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct RawProblem {
- data: Data,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct Data {
- question: Question,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct Question {
- content: String,
- stats: String,
- #[serde(rename = "codeDefinition")]
- code_definition: String,
- #[serde(rename = "sampleTestCase")]
- sample_test_case: String,
- #[serde(rename = "metaData")]
- meta_data: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Problems {
- user_name: String,
- num_solved: u32,
- num_total: u32,
- ac_easy: u32,
- ac_medium: u32,
- ac_hard: u32,
- pub stat_status_pairs: Vec,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct StatWithStatus {
- pub stat: Stat,
- difficulty: Difficulty,
- paid_only: bool,
- is_favor: bool,
- frequency: u32,
- progress: u32,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Stat {
- question_id: u32,
- #[serde(rename = "question__article__slug")]
- question_article_slug: Option,
- #[serde(rename = "question__title")]
- question_title: Option,
- #[serde(rename = "question__title_slug")]
- question_title_slug: Option,
- #[serde(rename = "question__hide")]
- question_hide: bool,
- total_acs: u32,
- total_submitted: u32,
- pub frontend_question_id: String,
- is_new_question: bool,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct Difficulty {
- level: u32,
-}
-
-impl Display for Difficulty {
- fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
- match self.level {
- 1 => f.write_str("Easy"),
- 2 => f.write_str("Medium"),
- 3 => f.write_str("Hard"),
- _ => f.write_str("Unknown"),
- }
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 55b0298..2f338b3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,3 @@
#[macro_use]
pub mod util;
-
-pub mod solution;
pub mod problem;
diff --git a/src/main.rs b/src/main.rs
index 4842082..9778ce6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,366 +1,65 @@
-#[macro_use]
-extern crate serde_derive;
-#[macro_use]
-extern crate serde_json;
-
-mod fetcher;
-
-use crate::fetcher::{CodeDefinition, Problem};
-use regex::Regex;
use std::fs;
-use std::fs::File;
-use std::io;
-use std::io::{BufRead, Write};
+use std::io::Write;
use std::path::Path;
+use crate::fetch_problem::{Fetcher, ProblemManager};
-use futures::executor::block_on;
-use futures::executor::ThreadPool;
-use futures::future::join_all;
-use futures::stream::StreamExt;
-use futures::task::SpawnExt;
-use std::sync::{Arc, Mutex};
+mod fetch_problem;
-/// main() helps to generate the submission template .rs
#[tokio::main]
-async fn main() {
- println!("Welcome to leetcode-rust system.\n");
- let mut initialized_ids = get_initialized_ids();
- loop {
- println!(
- "Please enter a frontend problem id, \n\
- or \"random\" to generate a random one, \n\
- or \"solve $i\" to move problem to solution/, \n\
- or \"all\" to initialize all problems \n"
- );
- let mut is_random = false;
- let mut is_solving = false;
- let mut id: u32 = 0;
- let mut id_arg = String::new();
- io::stdin()
- .read_line(&mut id_arg)
- .expect("Failed to read line");
- let id_arg = id_arg.trim();
+async fn main() {
+ let manager = ProblemManager::scan().expect("Failed to scan mod file.");
+ let fetcher = Fetcher::new();
- let random_pattern = Regex::new(r"^random$").unwrap();
- let solving_pattern = Regex::new(r"^solve (\d+)$").unwrap();
- let all_pattern = Regex::new(r"^all$").unwrap();
+ let args: Vec = std::env::args().collect();
+ let operation = &args[1].as_str();
- if random_pattern.is_match(id_arg) {
- println!("You select random mode.");
- id = generate_random_id(&initialized_ids);
- is_random = true;
- println!("Generate random problem: {}", &id);
- } else if solving_pattern.is_match(id_arg) {
- // solve a problem
- // move it from problem/ to solution/
- is_solving = true;
- id = solving_pattern
- .captures(id_arg)
- .unwrap()
- .get(1)
- .unwrap()
- .as_str()
- .parse()
- .unwrap();
- deal_solving(&id).await;
- break;
- } else if all_pattern.is_match(id_arg) {
- // deal all problems
- let pool = ThreadPool::new().unwrap();
- let mut tasks = vec![];
- let problems = fetcher::get_problems().await.unwrap();
- let mut mod_file_addon = Arc::new(Mutex::new(vec![]));
- for problem_stat in problems.stat_status_pairs {
- match problem_stat.stat.frontend_question_id.parse::() {
- Ok(id) => {
- if initialized_ids.contains(&id) {
- continue;
- }
+ match operation {
+ &"get" => {
+ let id = args[2].parse::();
+
+ match id {
+ Ok(id) => {
+ if manager.problem_list.contains(&id) {
+ eprintln!("Problem {} exists.", id);
+ return;
}
- _ => {}
- };
- let mod_file_addon = mod_file_addon.clone();
- tasks.push(
- pool.spawn_with_handle(async move {
- let problem = fetcher::get_problem_async(problem_stat).await;
- if problem.is_none() {
- return;
- }
- let problem = problem.unwrap();
- let code = problem
- .code_definition
- .iter()
- .find(|&d| d.value == "rust".to_string());
- if code.is_none() {
- println!("Problem {} has no rust version.", problem.question_id);
- return;
- }
- // not sure this can be async
- async {
- mod_file_addon.lock().unwrap().push(format!(
- "mod p{:04}_{};",
- problem.question_id,
- problem.title_slug.replace("-", "_")
- ));
- }
- .await;
- let code = code.unwrap();
- // not sure this can be async
- // maybe should use async-std io
- async { deal_problem(&problem, &code, false) }.await
- })
- .unwrap(),
- );
- }
- block_on(join_all(tasks));
- let mut lib_file = fs::OpenOptions::new()
- .write(true)
- .append(true)
- .open("./src/problem/mod.rs")
- .unwrap();
- writeln!(lib_file, "{}", mod_file_addon.lock().unwrap().join("\n"));
- break;
- } else {
- id = id_arg
- .parse::()
- .unwrap_or_else(|_| panic!("not a number: {}", id_arg));
- if initialized_ids.contains(&id) {
- println!("The problem you chose has been initialized in problem/");
- continue;
+
+ println!("Try to get problem {}...", id);
+
+ let problem = fetcher.get_problem(id).await
+ .expect(&*format!("Failed to get problem {}.", id));
+
+ let file_name = problem.get_filename();
+ println!("Get problem: {}.", file_name);
+ let content = problem.get_file_content().expect("Failed to format file content");
+
+ write_file(&file_name, &content).expect("Failed to write problem file.");
+ }
+ Err(_) => {
+ eprintln!("Get operation needs a usize param.")
+ }
}
}
-
- let problem = fetcher::get_problem(id).await.unwrap_or_else(|| {
- panic!(
- "Error: failed to get problem #{} \
- (The problem may be paid-only or may not be exist).",
- id
- )
- });
- let code = problem
- .code_definition
- .iter()
- .find(|&d| d.value == "rust".to_string());
- if code.is_none() {
- println!("Problem {} has no rust version.", &id);
- initialized_ids.push(problem.question_id);
- continue;
- }
- let code = code.unwrap();
- deal_problem(&problem, &code, true);
- break;
+ _ => {}
}
}
-fn generate_random_id(except_ids: &[u32]) -> u32 {
- use rand::Rng;
- let mut rng = rand::thread_rng();
- loop {
- let res: u32 = rng.gen_range(1, 1106);
- if !except_ids.contains(&res) {
- return res;
- }
- println!(
- "Generate a random num ({}), but it is invalid (the problem may have been solved \
- or may have no rust version). Regenerate..",
- res
- );
- }
-}
-
-fn get_initialized_ids() -> Vec {
- let content = fs::read_to_string("./src/problem/mod.rs").unwrap();
- let id_pattern = Regex::new(r"p(\d{4})_").unwrap();
- id_pattern
- .captures_iter(&content)
- .map(|x| x.get(1).unwrap().as_str().parse().unwrap())
- .collect()
-}
-
-fn parse_extra_use(code: &str) -> String {
- let mut extra_use_line = String::new();
- // a linked-list problem
- if code.contains("pub struct ListNode") {
- extra_use_line.push_str("\nuse crate::util::linked_list::{ListNode, to_list};")
- }
- if code.contains("pub struct TreeNode") {
- extra_use_line.push_str("\nuse crate::util::tree::{TreeNode, to_tree};")
- }
- if code.contains("pub struct Point") {
- extra_use_line.push_str("\nuse crate::util::point::Point;")
- }
- extra_use_line
-}
-
-fn parse_problem_link(problem: &Problem) -> String {
- format!("https://leetcode.cn/problems/{}/", problem.title_slug)
-}
-
-fn parse_discuss_link(problem: &Problem) -> String {
- format!(
- "https://leetcode.cn/problems/{}/discuss/?currentPage=1&orderBy=most_votes&query=",
- problem.title_slug
- )
-}
-
-fn insert_return_in_code(return_type: &str, code: &str) -> String {
- let re = Regex::new(r"\{[ \n]+}").unwrap();
- match return_type {
- "ListNode" => re
- .replace(&code, "{\n Some(Box::new(ListNode::new(0)))\n }")
- .to_string(),
- "ListNode[]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "TreeNode" => re
- .replace(
- &code,
- "{\n Some(Rc::new(RefCell::new(TreeNode::new(0))))\n }",
- )
- .to_string(),
- "boolean" => re.replace(&code, "{\n false\n }").to_string(),
- "character" => re.replace(&code, "{\n '0'\n }").to_string(),
- "character[][]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "double" => re.replace(&code, "{\n 0f64\n }").to_string(),
- "double[]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "int[]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "integer" => re.replace(&code, "{\n 0\n }").to_string(),
- "integer[]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "integer[][]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list>" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list>" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "list" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "null" => code.to_string(),
- "string" => re
- .replace(&code, "{\n String::new()\n }")
- .to_string(),
- "string[]" => re.replace(&code, "{\n vec![]\n }").to_string(),
- "void" => code.to_string(),
- "NestedInteger" => code.to_string(),
- "Node" => code.to_string(),
- _ => code.to_string(),
- }
-}
-
-fn build_desc(content: &str) -> String {
- // TODO: fix this shit
- content
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("
", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("
", "")
- .replace("
", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("
", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("", "")
- .replace("", "^")
- .replace(" ", " ")
- .replace(">", ">")
- .replace("<", "<")
- .replace(""", "\"")
- .replace("−", "-")
- .replace("'", "'")
- .replace("\n\n", "\n")
- .replace("\n", "\n * ")
-}
-
-async fn deal_solving(id: &u32) {
- let problem = fetcher::get_problem(*id).await.unwrap();
- let file_name = format!(
- "p{:04}_{}",
- problem.question_id,
- problem.title_slug.replace("-", "_")
- );
+fn write_file(file_name: &String, file_content: &String) -> std::io::Result<()> {
let file_path = Path::new("./src/problem").join(format!("{}.rs", file_name));
- // check problem/ existence
- if !file_path.exists() {
- panic!("problem does not exist");
- }
- // check solution/ no existence
- let solution_name = format!(
- "s{:04}_{}",
- problem.question_id,
- problem.title_slug.replace("-", "_")
- );
- let solution_path = Path::new("./src/solution").join(format!("{}.rs", solution_name));
- if solution_path.exists() {
- panic!("solution exists");
- }
- // rename/move file
- fs::rename(file_path, solution_path).unwrap();
- // remove from problem/mod.rs
- let mod_file = "./src/problem/mod.rs";
- let target_line = format!("mod {};", file_name);
- let lines: Vec = io::BufReader::new(File::open(mod_file).unwrap())
- .lines()
- .map(|x| x.unwrap())
- .filter(|x| *x != target_line)
- .collect();
- fs::write(mod_file, lines.join("\n"));
- // insert into solution/mod.rs
- let mut lib_file = fs::OpenOptions::new()
- .append(true)
- .open("./src/solution/mod.rs")
- .unwrap();
- writeln!(lib_file, "mod {};", solution_name);
-}
-
-fn deal_problem(problem: &Problem, code: &CodeDefinition, write_mod_file: bool) {
- let file_name = format!(
- "p{:04}_{}",
- problem.question_id,
- problem.title_slug.replace("-", "_")
- );
- let file_path = Path::new("./src/problem").join(format!("{}.rs", file_name));
- if file_path.exists() {
- panic!("problem already initialized");
- }
-
- let template = fs::read_to_string("./template.rs").unwrap();
- let source = template
- .replace("__PROBLEM_TITLE__", &problem.title)
- .replace("__PROBLEM_DESC__", &build_desc(&problem.content))
- .replace(
- "__PROBLEM_DEFAULT_CODE__",
- &insert_return_in_code(&problem.return_type, &code.default_code),
- )
- .replace("__PROBLEM_ID__", &format!("{}", problem.question_id))
- .replace("__EXTRA_USE__", &parse_extra_use(&code.default_code))
- .replace("__PROBLEM_LINK__", &parse_problem_link(problem))
- .replace("__DISCUSS_LINK__", &parse_discuss_link(problem));
let mut file = fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
- .open(&file_path)
- .unwrap();
+ .open(&file_path)?;
- file.write_all(source.as_bytes()).unwrap();
+ file.write_all(file_content.as_bytes())?;
drop(file);
- if write_mod_file {
- let mut lib_file = fs::OpenOptions::new()
- .write(true)
- .append(true)
- .open("./src/problem/mod.rs")
- .unwrap();
- writeln!(lib_file, "mod {};", file_name);
- }
-}
+ let mut mod_file = fs::OpenOptions::new()
+ .write(true)
+ .append(true)
+ .open("./src/problem/mod.rs")?;
+
+ write!(mod_file, "\nmod {};", file_name)
+}
\ No newline at end of file