From d49f192835c44a6ec152a9e59e55d02b552c4f29 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sat, 7 Dec 2024 21:40:03 +0900 Subject: [PATCH 01/10] add arg --- src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.rs b/src/main.rs index eeb1883e..cd97b1f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,10 @@ struct Args { /// Only use this if Rustlings fails to detect exercise file changes. #[arg(long)] manual_run: bool, + + /// Change rustbook url to passed one. + #[arg(long)] + base_url: Option, } #[derive(Subcommand)] From 96816a7b7847bd18cb63b912108cb13010bc07b1 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sat, 7 Dec 2024 21:40:18 +0900 Subject: [PATCH 02/10] try subcommand --- src/main.rs | 9 +++++++-- src/url_replacer.rs | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/url_replacer.rs diff --git a/src/main.rs b/src/main.rs index cd97b1f7..6d835b4c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::{ }; use term::{clear_terminal, press_enter_prompt}; -use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile}; +use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, url_replacer::UrlReplacer}; mod app_state; mod cargo_toml; @@ -22,6 +22,7 @@ mod list; mod run; mod term; mod watch; +mod url_replacer; const CURRENT_FORMAT_VERSION: u8 = 1; @@ -100,6 +101,8 @@ fn main() -> Result { info_file.final_message.unwrap_or_default(), )?; + let replacer = UrlReplacer::new(&args.base_url); + // Show the welcome message if the state file doesn't exist yet. if let Some(welcome_message) = info_file.welcome_message { match state_file_status { @@ -184,7 +187,9 @@ fn main() -> Result { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } - println!("{}", app_state.current_exercise().hint); + + let hint = app_state.current_exercise().hint; + println!("{}", replacer.replace(hint)); } // Handled in an earlier match. Some(Subcommands::Init | Subcommands::Dev(_)) => (), diff --git a/src/url_replacer.rs b/src/url_replacer.rs new file mode 100644 index 00000000..5b63e390 --- /dev/null +++ b/src/url_replacer.rs @@ -0,0 +1,21 @@ +pub struct UrlReplacer <'a> { + base_url: &'a Option +} + +const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; + +impl <'a> UrlReplacer <'a> { + pub fn new(base_url: &'a Option) -> Self { + Self { + base_url + } + } + + pub fn replace(&self, hint: &str) -> String { + if let Some(base_url) = self.base_url { + hint.replace(EN_BASE_URL, base_url) + } else { + hint.to_owned() + } + } +} From 2c093410745fea46c7405307d9095b1c18d00a49 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sat, 7 Dec 2024 22:01:18 +0900 Subject: [PATCH 03/10] to contain String --- src/main.rs | 11 +++++++---- src/url_replacer.rs | 14 +++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6d835b4c..c27bfa89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,8 +101,6 @@ fn main() -> Result { info_file.final_message.unwrap_or_default(), )?; - let replacer = UrlReplacer::new(&args.base_url); - // Show the welcome message if the state file doesn't exist yet. if let Some(welcome_message) = info_file.welcome_message { match state_file_status { @@ -187,9 +185,14 @@ fn main() -> Result { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } - + let hint = app_state.current_exercise().hint; - println!("{}", replacer.replace(hint)); + if let Some(base_url) = args.base_url { + let replacer = UrlReplacer::new(base_url); + println!("{}", replacer.replace(hint)); + } else { + println!("{}", hint); + }; } // Handled in an earlier match. Some(Subcommands::Init | Subcommands::Dev(_)) => (), diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 5b63e390..dac8852d 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -1,21 +1,17 @@ -pub struct UrlReplacer <'a> { - base_url: &'a Option +pub struct UrlReplacer { + base_url: String } const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; -impl <'a> UrlReplacer <'a> { - pub fn new(base_url: &'a Option) -> Self { +impl UrlReplacer { + pub fn new(base_url: String) -> Self { Self { base_url } } pub fn replace(&self, hint: &str) -> String { - if let Some(base_url) = self.base_url { - hint.replace(EN_BASE_URL, base_url) - } else { - hint.to_owned() - } + hint.replace(EN_BASE_URL, &self.base_url) } } From 509d240d2b0583cebd291d10601b639a93886aa4 Mon Sep 17 00:00:00 2001 From: Kim Hokyeong Date: Sun, 8 Dec 2024 02:12:43 +0000 Subject: [PATCH 04/10] add unit test --- src/url_replacer.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/url_replacer.rs b/src/url_replacer.rs index dac8852d..119d2392 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -15,3 +15,41 @@ impl UrlReplacer { hint.replace(EN_BASE_URL, &self.base_url) } } + +#[cfg(test)] +mod test { + use super::*; + + const TEST_DOMAIN: &str = "https://doc.rust-kr.org"; + + #[test] + fn non_url() { + let replacer = UrlReplacer { + base_url: String::from(TEST_DOMAIN) + }; + + let hint = "\ +hints (...) lines (...) +link: https://doc.rust-lang.org/book/ch03-02-data-types.html"; + + assert_eq!("\ +hints (...) lines (...) +link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); + } + + + #[test] + fn replace_url() { + let replacer = UrlReplacer { + base_url: String::from(TEST_DOMAIN) + }; + + let hint = "\ +hints (...) lines (...) +link: https://doc.rust-lang.org/book/ch03-02-data-types.html"; + + assert_eq!("\ +hints (...) lines (...) +link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); + } +} \ No newline at end of file From 1ffe7fc8371b13f69259171abeb54874ddcf1ba2 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 11:16:02 +0900 Subject: [PATCH 05/10] fix --- src/url_replacer.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 119d2392..3288fb60 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -23,23 +23,21 @@ mod test { const TEST_DOMAIN: &str = "https://doc.rust-kr.org"; #[test] - fn non_url() { + fn non_rustbook_url() { let replacer = UrlReplacer { base_url: String::from(TEST_DOMAIN) }; let hint = "\ hints (...) lines (...) -link: https://doc.rust-lang.org/book/ch03-02-data-types.html"; +link: https://example.com/ch03-02-data-types.html"; - assert_eq!("\ -hints (...) lines (...) -link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); + assert_eq!(hint, replacer.replace(hint)); } #[test] - fn replace_url() { + fn replace_rustbook_url() { let replacer = UrlReplacer { base_url: String::from(TEST_DOMAIN) }; From 5cd711e909fa0983ad1e0ad8cd2c4c1a2de88d93 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 11:19:51 +0900 Subject: [PATCH 06/10] trim_end_with_slash --- src/url_replacer.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 3288fb60..97977305 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -5,7 +5,13 @@ pub struct UrlReplacer { const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; impl UrlReplacer { - pub fn new(base_url: String) -> Self { + pub fn new(mut base_url: String) -> Self { + base_url = if base_url.ends_with('/') { + base_url.trim_end_matches('/').to_owned() + } else { + base_url + }; + Self { base_url } @@ -24,9 +30,7 @@ mod test { #[test] fn non_rustbook_url() { - let replacer = UrlReplacer { - base_url: String::from(TEST_DOMAIN) - }; + let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); let hint = "\ hints (...) lines (...) @@ -38,9 +42,7 @@ link: https://example.com/ch03-02-data-types.html"; #[test] fn replace_rustbook_url() { - let replacer = UrlReplacer { - base_url: String::from(TEST_DOMAIN) - }; + let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); let hint = "\ hints (...) lines (...) @@ -50,4 +52,11 @@ link: https://doc.rust-lang.org/book/ch03-02-data-types.html"; hints (...) lines (...) link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); } + + #[test] + fn trim_end_with_slash() { + let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); + + assert_eq!(TEST_DOMAIN, replacer.base_url); + } } \ No newline at end of file From 67605609209574cb1991408f60db39defa1dd738 Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 11:44:13 +0900 Subject: [PATCH 07/10] hold replacer in app_state insted of main / watcher --- src/app_state.rs | 16 ++++++++++++++-- src/main.rs | 11 +++-------- src/url_replacer.rs | 19 ++++++++++++------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 5979150f..7de17543 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -20,7 +20,7 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term::{self, CheckProgressVisualizer}, + term::{self, CheckProgressVisualizer}, url_replacer::UrlReplacer, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; @@ -68,6 +68,7 @@ impl AppState { pub fn new( exercise_infos: Vec, final_message: String, + base_url: Option, ) -> Result<(Self, StateFileStatus)> { let cmd_runner = CmdRunner::build()?; let mut state_file = OpenOptions::new() @@ -80,6 +81,13 @@ impl AppState { format!("Failed to open or create the state file {STATE_FILE_NAME}") })?; + // replacer for rustbook url + let url_replacer = if let Some(url) = &base_url { + Some(UrlReplacer::new(&url)) + } else { + None + }; + let dir_canonical_path = term::canonicalize("exercises"); let mut exercises = exercise_infos .into_iter() @@ -90,7 +98,11 @@ impl AppState { let path = exercise_info.path().leak(); let name = exercise_info.name.leak(); let dir = exercise_info.dir.map(|dir| &*dir.leak()); - let hint = exercise_info.hint.leak().trim_ascii(); + let mut hint = exercise_info.hint.leak().trim_ascii(); + + if let Some(replacer) = &url_replacer { + hint = replacer.replace(&hint).leak(); + } let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| { let mut canonical_path; diff --git a/src/main.rs b/src/main.rs index c27bfa89..2ee376b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::{ }; use term::{clear_terminal, press_enter_prompt}; -use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, url_replacer::UrlReplacer}; +use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile}; mod app_state; mod cargo_toml; @@ -99,6 +99,7 @@ fn main() -> Result { let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), + args.base_url, )?; // Show the welcome message if the state file doesn't exist yet. @@ -186,13 +187,7 @@ fn main() -> Result { app_state.set_current_exercise_by_name(&name)?; } - let hint = app_state.current_exercise().hint; - if let Some(base_url) = args.base_url { - let replacer = UrlReplacer::new(base_url); - println!("{}", replacer.replace(hint)); - } else { - println!("{}", hint); - }; + println!("{}", app_state.current_exercise().hint); } // Handled in an earlier match. Some(Subcommands::Init | Subcommands::Dev(_)) => (), diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 97977305..b32a8495 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -5,18 +5,20 @@ pub struct UrlReplacer { const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; impl UrlReplacer { - pub fn new(mut base_url: String) -> Self { - base_url = if base_url.ends_with('/') { + /// this fn will trim url end with '/' + pub fn new(base_url: &String) -> Self { + let url = if base_url.ends_with('/') { base_url.trim_end_matches('/').to_owned() } else { - base_url + base_url.clone() }; Self { - base_url + base_url: url } } + /// replace rustbook url pub fn replace(&self, hint: &str) -> String { hint.replace(EN_BASE_URL, &self.base_url) } @@ -30,7 +32,7 @@ mod test { #[test] fn non_rustbook_url() { - let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); + let replacer = UrlReplacer::new(&String::from(TEST_DOMAIN)); let hint = "\ hints (...) lines (...) @@ -42,7 +44,7 @@ link: https://example.com/ch03-02-data-types.html"; #[test] fn replace_rustbook_url() { - let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); + let replacer = UrlReplacer::new(&String::from(TEST_DOMAIN)); let hint = "\ hints (...) lines (...) @@ -55,7 +57,10 @@ link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); #[test] fn trim_end_with_slash() { - let replacer = UrlReplacer::new(String::from(TEST_DOMAIN)); + let mut domain = String::from(TEST_DOMAIN); + domain.push('/'); + + let replacer = UrlReplacer::new(&domain); assert_eq!(TEST_DOMAIN, replacer.base_url); } From 027cbdb629be36bc2da5a39061d899ccedf3099f Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 11:46:56 +0900 Subject: [PATCH 08/10] format / reduce diff --- src/app_state.rs | 3 ++- src/main.rs | 1 - src/url_replacer.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 7de17543..373a9872 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -20,7 +20,8 @@ use crate::{ embedded::EMBEDDED_FILES, exercise::{Exercise, RunnableExercise}, info_file::ExerciseInfo, - term::{self, CheckProgressVisualizer}, url_replacer::UrlReplacer, + term::{self, CheckProgressVisualizer}, + url_replacer::UrlReplacer, }; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; diff --git a/src/main.rs b/src/main.rs index 2ee376b4..a7941d28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -186,7 +186,6 @@ fn main() -> Result { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; } - println!("{}", app_state.current_exercise().hint); } // Handled in an earlier match. diff --git a/src/url_replacer.rs b/src/url_replacer.rs index b32a8495..6a4aac45 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -64,4 +64,4 @@ link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); assert_eq!(TEST_DOMAIN, replacer.base_url); } -} \ No newline at end of file +} From e5f0a8f997d253ae8fd19c13adc8ab435a7fefeb Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 11:50:49 +0900 Subject: [PATCH 09/10] holy clippy --- src/app_state.rs | 10 ++++------ src/url_replacer.rs | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index 373a9872..e8d1bd1a 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -83,11 +83,9 @@ impl AppState { })?; // replacer for rustbook url - let url_replacer = if let Some(url) = &base_url { - Some(UrlReplacer::new(&url)) - } else { - None - }; + let url_replacer = base_url.as_ref().map(|url| { + UrlReplacer::new(url) + }); let dir_canonical_path = term::canonicalize("exercises"); let mut exercises = exercise_infos @@ -102,7 +100,7 @@ impl AppState { let mut hint = exercise_info.hint.leak().trim_ascii(); if let Some(replacer) = &url_replacer { - hint = replacer.replace(&hint).leak(); + hint = replacer.replace(hint).leak(); } let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| { diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 6a4aac45..0499372f 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -6,11 +6,11 @@ const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; impl UrlReplacer { /// this fn will trim url end with '/' - pub fn new(base_url: &String) -> Self { + pub fn new(base_url: &str) -> Self { let url = if base_url.ends_with('/') { base_url.trim_end_matches('/').to_owned() } else { - base_url.clone() + base_url.to_owned() }; Self { From 275071b740cabd833eb2d4fa851bcef1ce2851ff Mon Sep 17 00:00:00 2001 From: manmen-mi Date: Sun, 8 Dec 2024 12:05:21 +0900 Subject: [PATCH 10/10] `cargo fmt --all --check` --- src/app_state.rs | 4 +--- src/main.rs | 2 +- src/url_replacer.rs | 14 +++++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/app_state.rs b/src/app_state.rs index e8d1bd1a..1c623075 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -83,9 +83,7 @@ impl AppState { })?; // replacer for rustbook url - let url_replacer = base_url.as_ref().map(|url| { - UrlReplacer::new(url) - }); + let url_replacer = base_url.as_ref().map(|url| UrlReplacer::new(url)); let dir_canonical_path = term::canonicalize("exercises"); let mut exercises = exercise_infos diff --git a/src/main.rs b/src/main.rs index a7941d28..a46f9fa1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,8 +21,8 @@ mod init; mod list; mod run; mod term; -mod watch; mod url_replacer; +mod watch; const CURRENT_FORMAT_VERSION: u8 = 1; diff --git a/src/url_replacer.rs b/src/url_replacer.rs index 0499372f..3a3537a5 100644 --- a/src/url_replacer.rs +++ b/src/url_replacer.rs @@ -1,5 +1,5 @@ pub struct UrlReplacer { - base_url: String + base_url: String, } const EN_BASE_URL: &str = "https://doc.rust-lang.org/book"; @@ -13,9 +13,7 @@ impl UrlReplacer { base_url.to_owned() }; - Self { - base_url: url - } + Self { base_url: url } } /// replace rustbook url @@ -41,7 +39,6 @@ link: https://example.com/ch03-02-data-types.html"; assert_eq!(hint, replacer.replace(hint)); } - #[test] fn replace_rustbook_url() { let replacer = UrlReplacer::new(&String::from(TEST_DOMAIN)); @@ -50,9 +47,12 @@ link: https://example.com/ch03-02-data-types.html"; hints (...) lines (...) link: https://doc.rust-lang.org/book/ch03-02-data-types.html"; - assert_eq!("\ + assert_eq!( + "\ hints (...) lines (...) -link: https://doc.rust-kr.org/ch03-02-data-types.html", replacer.replace(hint)); +link: https://doc.rust-kr.org/ch03-02-data-types.html", + replacer.replace(hint) + ); } #[test]