From 44fc93af91ce90d8f627c880a1939ea364e15157 Mon Sep 17 00:00:00 2001 From: TimLai666 <43640816+TimLai666@users.noreply.github.com> Date: Mon, 10 Jun 2024 18:23:38 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BF=BB=E8=AD=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/exercise.rs | 104 +++++++++++++++--------------- src/main.rs | 167 ++++++++++++++++++++++-------------------------- src/project.rs | 25 ++++---- src/run.rs | 25 ++++---- src/verify.rs | 65 ++++++++++--------- 5 files changed, 182 insertions(+), 204 deletions(-) diff --git a/src/exercise.rs b/src/exercise.rs index 19f528a8..6101b346 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -15,7 +15,7 @@ const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const CONTEXT: usize = 2; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; -// Checks if the line contains the "I AM NOT DONE" comment. +// 檢查該行是否包含 "I AM NOT DONE" 註釋。 fn contains_not_done_comment(input: &str) -> bool { ( space0::<_, ()>, @@ -28,7 +28,7 @@ fn contains_not_done_comment(input: &str) -> bool { .is_ok() } -// Get a temporary file name that is hopefully unique +// 獲取一個臨時文件名,這個文件名應該是唯一的 #[inline] fn temp_file() -> String { let thread_id: String = format!("{:?}", std::thread::current().id()) @@ -39,15 +39,15 @@ fn temp_file() -> String { format!("./temp_{}_{thread_id}", process::id()) } -// The mode of the exercise. +// 練習的模式。 #[derive(Deserialize, Copy, Clone, Debug)] #[serde(rename_all = "lowercase")] pub enum Mode { - // Indicates that the exercise should be compiled as a binary + // 表示練習應該編譯為二進制文件 Compile, - // Indicates that the exercise should be compiled as a test harness + // 表示練習應該編譯為測試框架 Test, - // Indicates that the exercise should be linted with clippy + // 表示練習應該使用 clippy 進行檢查 Clippy, } @@ -56,60 +56,60 @@ pub struct ExerciseList { pub exercises: Vec, } -// A representation of a rustlings exercise. -// This is deserialized from the accompanying info.toml file +// Rustlings 練習的表示。 +// 這是從伴隨的 info.toml 文件中反序列化而來的 #[derive(Deserialize, Debug)] pub struct Exercise { - // Name of the exercise + // 練習的名稱 pub name: String, - // The path to the file containing the exercise's source code + // 包含練習源代碼的文件的路徑 pub path: PathBuf, - // The mode of the exercise (Test, Compile, or Clippy) + // 練習的模式(Test、Compile 或 Clippy) pub mode: Mode, - // The hint text associated with the exercise + // 與練習相關的提示文字 pub hint: String, } -// An enum to track of the state of an Exercise. -// An Exercise can be either Done or Pending +// 用於跟踪練習狀態的枚舉。 +// 練習可以是 Done 或 Pending 狀態 #[derive(PartialEq, Eq, Debug)] pub enum State { - // The state of the exercise once it's been completed + // 表示練習已完成的狀態 Done, - // The state of the exercise while it's not completed yet + // 表示練習尚未完成的狀態 Pending(Vec), } -// The context information of a pending exercise +// 未完成練習的上下文信息 #[derive(PartialEq, Eq, Debug)] pub struct ContextLine { - // The source code that is still pending completion + // 尚未完成的源代碼 pub line: String, - // The line number of the source code still pending completion + // 尚未完成的源代碼行號 pub number: usize, - // Whether or not this is important + // 是否重要 pub important: bool, } -// The result of compiling an exercise +// 編譯練習的結果 pub struct CompiledExercise<'a> { exercise: &'a Exercise, _handle: FileHandle, } impl<'a> CompiledExercise<'a> { - // Run the compiled exercise + // 運行已編譯的練習 pub fn run(&self) -> Result { self.exercise.run() } } -// A representation of an already executed binary +// 已執行二進制文件的表示 #[derive(Debug)] pub struct ExerciseOutput { - // The textual contents of the standard output of the binary + // 二進制文件標準輸出的文本內容 pub stdout: String, - // The textual contents of the standard error of the binary + // 二進制文件標準錯誤的文本內容 pub stderr: String, } @@ -153,10 +153,8 @@ path = "{}.rs""#, "Failed to write 📎 Clippy 📎 Cargo.toml file." }; fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg); - // To support the ability to run the clippy exercises, build - // an executable, in addition to running clippy. With a - // compilation failure, this would silently fail. But we expect - // clippy to reflect the same failure while compiling later. + // 為了支持運行 clippy 練習,除了運行 clippy,還要構建可執行文件。 + // 如果編譯失敗,這將靜默失敗。但我們期望 clippy 在稍後編譯時反映相同的失敗。 Command::new("rustc") .args([self.path.to_str().unwrap(), "-o", &temp_file()]) .args(RUSTC_COLOR_ARGS) @@ -167,9 +165,9 @@ path = "{}.rs""#, .stderr(Stdio::null()) .status() .expect("Failed to compile!"); - // Due to an issue with Clippy, a cargo clean is required to catch all lints. - // See https://github.com/rust-lang/rust-clippy/issues/2604 - // This is already fixed on Clippy's master branch. See this issue to track merging into Cargo: + // 由於 Clippy 的一個問題,需要進行 cargo clean 以捕獲所有 lint。 + // 參見 https://github.com/rust-lang/rust-clippy/issues/2604 + // 這已在 Clippy 的主分支中修復。請參見此問題以跟踪合併到 Cargo 中: // https://github.com/rust-lang/rust-clippy/issues/3837 Command::new("cargo") .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) @@ -227,14 +225,14 @@ path = "{}.rs""#, pub fn state(&self) -> State { let source_file = File::open(&self.path).unwrap_or_else(|e| { println!( - "Failed to open the exercise file {}: {e}", + "無法打開練習文件 {}: {e}", self.path.display(), ); exit(1); }); let mut source_reader = BufReader::new(source_file); - // Read the next line into `buf` without the newline at the end. + // 將下一行讀入 `buf`,但末尾沒有換行符。 let mut read_line = |buf: &mut String| -> io::Result<_> { let n = source_reader.read_line(buf)?; if buf.ends_with('\n') { @@ -247,27 +245,27 @@ path = "{}.rs""#, }; let mut current_line_number: usize = 1; - // Keep the last `CONTEXT` lines while iterating over the file lines. + // 在遍歷文件行時保留最後的 `CONTEXT` 行。 let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); let mut line = String::with_capacity(256); loop { let n = read_line(&mut line).unwrap_or_else(|e| { println!( - "Failed to read the exercise file {}: {e}", + "讀取練習文件 {} 失敗: {e}", self.path.display(), ); exit(1); }); - // Reached the end of the file and didn't find the comment. + // 到達文件末尾但未找到註釋。 if n == 0 { return State::Done; } if contains_not_done_comment(&line) { let mut context = Vec::with_capacity(2 * CONTEXT + 1); - // Previous lines. + // 之前的行。 for (ind, prev_line) in prev_lines .into_iter() .take(current_line_number - 1) @@ -281,22 +279,22 @@ path = "{}.rs""#, }); } - // Current line. + // 當前行。 context.push(ContextLine { line, number: current_line_number, important: true, }); - // Next lines. + // 後續行。 for ind in 0..CONTEXT { let mut next_line = String::with_capacity(256); let Ok(n) = read_line(&mut next_line) else { - // If an error occurs, just ignore the next lines. + // 如果發生錯誤,只需忽略後續行。 break; }; - // Reached the end of the file. + // 到達文件末尾。 if n == 0 { break; } @@ -312,22 +310,22 @@ path = "{}.rs""#, } current_line_number += 1; - // Add the current line as a previous line and shift the older lines by one. + // 將當前行添加為前一行,並將較舊的行向後移動一行。 for prev_line in &mut prev_lines { mem::swap(&mut line, prev_line); } - // The current line now contains the oldest previous line. - // Recycle it for reading the next line. + // 當前行現在包含最舊的前一行。 + // 將其回收以讀取下一行。 line.clear(); } } - // Check that the exercise looks to be solved using self.state() - // This is not the best way to check since - // the user can just remove the "I AM NOT DONE" string from the file - // without actually having solved anything. - // The only other way to truly check this would to compile and run - // the exercise; which would be both costly and counterintuitive + // 使用 self.state() 檢查練習看起來是否已解決 + // 這不是最好的檢查方法,因為 + // 用戶可以僅從文件中刪除 "I AM NOT DONE" 字符串 + // 而實際上並沒有解決任何問題。 + // 唯一真正檢查的方法是編譯並運行 + // 練習;這既昂貴又違反直覺 pub fn looks_done(&self) -> bool { self.state() == State::Done } @@ -366,12 +364,12 @@ mod test { #[test] #[cfg(target_os = "windows")] fn test_no_pdb_file() { - [Mode::Compile, Mode::Test] // Clippy doesn't like to test + [Mode::Compile, Mode::Test] // Clippy 不喜歡測試 .iter() .for_each(|mode| { let exercise = Exercise { name: String::from("example"), - // We want a file that does actually compile + // 我們需要一個確實可以編譯的文件 path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), mode: *mode, hint: String::from(""), diff --git a/src/main.rs b/src/main.rs index 8f73dbba..851b5525 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,11 +27,11 @@ mod project; mod run; mod verify; -/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code +/// Rustlings 是一組小練習,用來讓您習慣於編寫和閱讀 Rust 代碼 #[derive(Parser)] #[command(version)] struct Args { - /// Show outputs from the test exercises + /// 顯示測試練習的輸出 #[arg(long)] nocapture: bool, #[command(subcommand)] @@ -40,49 +40,49 @@ struct Args { #[derive(Subcommand)] enum Subcommands { - /// Verify all exercises according to the recommended order + /// 按推薦順序驗證所有練習 Verify, - /// Rerun `verify` when files were edited + /// 在文件編輯後重新運行 `verify` Watch { - /// Show hints on success + /// 成功時顯示提示 #[arg(long)] success_hints: bool, }, - /// Run/Test a single exercise + /// 運行/測試單個練習 Run { - /// The name of the exercise + /// 練習的名稱 name: String, }, - /// Reset a single exercise using "git stash -- " + /// 使用 "git stash -- " 重置單個練習 Reset { - /// The name of the exercise + /// 練習的名稱 name: String, }, - /// Return a hint for the given exercise + /// 返回指定練習的提示 Hint { - /// The name of the exercise + /// 練習的名稱 name: String, }, - /// List the exercises available in Rustlings + /// 列出 Rustlings 中可用的練習 List { - /// Show only the paths of the exercises + /// 僅顯示練習的路徑 #[arg(short, long)] paths: bool, - /// Show only the names of the exercises + /// 僅顯示練習的名稱 #[arg(short, long)] names: bool, - /// Provide a string to match exercise names. - /// Comma separated patterns are accepted + /// 提供一個字符串來匹配練習名稱。 + /// 接受逗號分隔的模式 #[arg(short, long)] filter: Option, - /// Display only exercises not yet solved + /// 僅顯示尚未解決的練習 #[arg(short, long)] unsolved: bool, - /// Display only exercises that have been solved + /// 僅顯示已經解決的練習 #[arg(short, long)] solved: bool, }, - /// Enable rust-analyzer for exercises + /// 啟用 rust-analyzer 用於練習 Lsp, } @@ -94,18 +94,18 @@ fn main() -> Result<()> { } if which::which("rustc").is_err() { - println!("We cannot find `rustc`."); - println!("Try running `rustc --version` to diagnose your problem."); - println!("For instructions on how to install Rust, check the README."); + println!("我們無法找到 `rustc`。"); + println!("嘗試運行 `rustc --version` 來診斷您的問題。"); + println!("有關如何安裝 Rust 的說明,請查看 README。"); std::process::exit(1); } let info_file = fs::read_to_string("info.toml").unwrap_or_else(|e| { match e.kind() { io::ErrorKind::NotFound => println!( - "The program must be run from the rustlings directory\nTry `cd rustlings/`!", + "程序必須在 rustlings 目錄中運行\n嘗試 `cd rustlings/`!", ), - _ => println!("Failed to read the info.toml file: {e}"), + _ => println!("讀取 info.toml 文件失敗: {e}"), } std::process::exit(1); }); @@ -128,7 +128,7 @@ fn main() -> Result<()> { solved, } => { if !paths && !names { - println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); + println!("{:<17}\t{:<46}\t{:<7}", "名稱", "路徑", "狀態"); } let mut exercises_done: u16 = 0; let lowercase_filter = filter @@ -155,9 +155,9 @@ fn main() -> Result<()> { let looks_done = exercise.looks_done(); let status = if looks_done { exercises_done += 1; - "Done" + "已完成" } else { - "Pending" + "未完成" }; let solve_cond = (looks_done && solved) || (!looks_done && unsolved) || (!solved && !unsolved); @@ -169,9 +169,8 @@ fn main() -> Result<()> { } else { format!("{:<17}\t{fname:<46}\t{status:<7}\n", exercise.name) }; - // Somehow using println! leads to the binary panicking - // when its output is piped. - // So, we're handling a Broken Pipe error and exiting with 0 anyway + // 不知為何,使用 println! 在其輸出被管道時會導致二進制文件恐慌 + // 因此,我們處理了一個 Broken Pipe 錯誤並仍然以 0 退出 let stdout = std::io::stdout(); { let mut handle = stdout.lock(); @@ -187,7 +186,7 @@ fn main() -> Result<()> { let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; println!( - "Progress: You completed {} / {} exercises ({:.1} %).", + "進度: 您完成了 {} / {} 個練習 ({:.1} %)。", exercises_done, exercises.len(), percentage_progress @@ -220,29 +219,29 @@ fn main() -> Result<()> { Subcommands::Lsp => { if let Err(e) = write_project_json(exercises) { - println!("Failed to write rust-project.json to disk for rust-analyzer: {e}"); + println!("無法將 rust-project.json 寫入磁碟以用於 rust-analyzer: {e}"); } else { - println!("Successfully generated rust-project.json"); - println!("rust-analyzer will now parse exercises, restart your language server or editor"); + println!("成功生成 rust-project.json"); + println!("rust-analyzer 現在將解析練習,重啟您的語言服務器或編輯器"); } } Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Err(e) => { - println!("Error: Could not watch your progress. Error message was {e:?}."); - println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); + println!("錯誤: 無法監視您的進度。錯誤訊息為 {e:?}。"); + println!("最有可能是您的磁碟空間已滿或您的 'inotify 限制' 已達到。"); std::process::exit(1); } Ok(WatchStatus::Finished) => { println!( - "{emoji} All exercises completed! {emoji}", + "{emoji} 所有練習都完成了! {emoji}", emoji = Emoji("🎉", "★") ); println!("\n{FENISH_LINE}\n"); } Ok(WatchStatus::Unfinished) => { - println!("We hope you're enjoying learning about Rust!"); - println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); + println!("我們希望您享受學習 Rust 的過程!"); + println!("如果您想在稍後繼續完成這些練習,只需再次運行 `rustlings watch`"); } }, } @@ -254,18 +253,18 @@ fn spawn_watch_shell( failed_exercise_hint: Arc>>, should_quit: Arc, ) { - println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); + println!("歡迎來到 watch 模式!您可以輸入 'help' 來獲取此處可用命令的概覽。"); thread::spawn(move || { let mut input = String::with_capacity(32); let mut stdin = io::stdin().lock(); loop { - // Recycle input buffer. + // 回收輸入緩衝區。 input.clear(); if let Err(e) = stdin.read_line(&mut input) { - println!("error reading command: {e}"); + println!("讀取命令錯誤: {e}"); } let input = input.trim(); @@ -277,22 +276,22 @@ fn spawn_watch_shell( println!("\x1B[2J\x1B[1;1H"); } else if input == "quit" { should_quit.store(true, Ordering::SeqCst); - println!("Bye!"); + println!("再見!"); } else if input == "help" { println!("{WATCH_MODE_HELP_MESSAGE}"); } else if let Some(cmd) = input.strip_prefix('!') { let mut parts = Shlex::new(cmd); let Some(program) = parts.next() else { - println!("no command provided"); + println!("未提供命令"); continue; }; if let Err(e) = Command::new(program).args(parts).status() { - println!("failed to execute command `{cmd}`: {e}"); + println!("執行命令 `{cmd}` 失敗: {e}"); } } else { - println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}"); + println!("未知命令: {input}\n{WATCH_MODE_HELP_MESSAGE}"); } } }); @@ -304,8 +303,8 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { .iter() .find(|e| !e.looks_done()) .unwrap_or_else(|| { - println!("🎉 Congratulations! You have done all the exercises!"); - println!("🔚 There are no more exercises to do next!"); + println!("🎉 恭喜!您已完成所有練習!"); + println!("🔚 沒有更多的練習可以做了!"); std::process::exit(1) }) } else { @@ -313,7 +312,7 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { .iter() .find(|e| e.name == name) .unwrap_or_else(|| { - println!("No exercise found for '{name}'!"); + println!("找不到名為 '{name}' 的練習!"); std::process::exit(1) }) } @@ -329,8 +328,8 @@ fn watch( verbose: bool, success_hints: bool, ) -> notify::Result { - /* Clears the terminal with an ANSI escape code. - Works in UNIX and newer Windows terminals. */ + /* 使用 ANSI 轉義碼清除終端。 + 適用於 UNIX 和較新的 Windows 終端。 */ fn clear_screen() { println!("\x1Bc"); } @@ -395,50 +394,35 @@ fn watch( } } } - Err(e) => println!("watch error: {e:?}"), + Err(e) => println!("監視錯誤: {e:?}"), }, Err(RecvTimeoutError::Timeout) => { - // the timeout expired, just check the `should_quit` variable below then loop again + // 超時了,只需檢查下面的 `should_quit` 變量,然後再次循環 } - Err(e) => println!("watch error: {e:?}"), + Err(e) => println!("監視錯誤: {e:?}"), } - // Check if we need to exit + // 檢查是否需要退出 if should_quit.load(Ordering::SeqCst) { return Ok(WatchStatus::Unfinished); } } } -const DEFAULT_OUT: &str = "Thanks for installing Rustlings! +const DEFAULT_OUT: &str = "感謝您安裝 Rustlings! -Is this your first time? Don't worry, Rustlings was made for beginners! We are -going to teach you a lot of things about Rust, but before we can get -started, here's a couple of notes about how Rustlings operates: +這是您第一次使用嗎?別擔心,Rustlings 是為初學者設計的!我們將教您很多關於 Rust 的知識,但在開始之前,這裡有一些關於 Rustlings 的操作注意事項: -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually have some sort of syntax error in them, which will cause - them to fail compilation or testing. Sometimes there's a logic error instead - of a syntax error. No matter what error, it's your job to find it and fix it! - You'll know when you fixed it because then, the exercise will compile and - Rustlings will be able to move on to the next exercise. -2. If you run Rustlings in watch mode (which we recommend), it'll automatically - start with the first exercise. Don't get confused by an error message popping - up as soon as you run Rustlings! This is part of the exercise that you're - supposed to solve, so open the exercise file in an editor and start your - detective work! -3. If you're stuck on an exercise, there is a helpful hint you can view by typing - 'hint' (in watch mode), or running `rustlings hint exercise_name`. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, - and sometimes, other learners do too so you can help each other out! -5. If you want to use `rust-analyzer` with exercises, which provides features like - autocompletion, run the command `rustlings lsp`. +1. Rustlings 的核心概念是讓您解決練習。這些練習通常會有某種語法錯誤,這會導致它們無法編譯或測試。有時會是邏輯錯誤而不是語法錯誤。無論是什麼錯誤,您的任務是找到並修復它! + 當您修復它時,您會知道,因為那時練習會編譯並且 Rustlings 將能夠進行到下一個練習。 +2. 如果您以 watch 模式運行 Rustlings(我們推薦這樣做),它會自動從第一個練習開始。剛運行 Rustlings 時出現錯誤消息不要感到困惑!這是您要解決的練習的一部分,因此在編輯器中打開練習文件並開始您的偵探工作吧! +3. 如果您在練習中遇到困難,可以通過輸入 'hint' 來查看提示(在 watch 模式下),或者運行 `rustlings hint exercise_name`。 +4. 如果一個練習對您來說沒有意義,請隨時在 GitHub 上打開一個問題!(https://github.com/rust-lang/rustlings/issues/new) 我們會查看每個問題,有時其他學習者也會這樣做,所以您可以互相幫助! +5. 如果您想在練習中使用 `rust-analyzer`,這會提供自動完成等功能,請運行命令 `rustlings lsp`。 -Got all that? Great! To get started, run `rustlings watch` in order to get the first -exercise. Make sure to have your editor open!"; +都記住了嗎?很好!要開始,請運行 `rustlings watch` 以獲取第一個練習。確保您的編輯器是開著的!"; const FENISH_LINE: &str = "+----------------------------------------------------+ -| You made it to the Fe-nish line! | +| 您已經到達 Fe-nish 線! | +-------------------------- ------------------------+ \\/\x1b[31m ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ @@ -457,11 +441,11 @@ const FENISH_LINE: &str = "+---------------------------------------------------- ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒\x1b[0m -We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, please don't hesitate to report them to our repo. -You can also contribute your own exercises to help the greater community! +我們希望您喜歡學習 Rust 的各個方面! +如果您發現任何問題,請隨時向我們的倉庫報告。 +您也可以貢獻您自己的練習來幫助更多的人! -Before reporting an issue or contributing, please read our guidelines: +在報告問題或貢獻之前,請閱讀我們的指南: https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; const WELCOME: &str = r" welcome to... @@ -472,12 +456,11 @@ const WELCOME: &str = r" welcome to... |_| \__,_|___/\__|_|_|_| |_|\__, |___/ |___/"; -const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: - hint - prints the current exercise's hint - clear - clears the screen - quit - quits watch mode - ! - executes a command, like `!rustc --explain E0381` - help - displays this help message +const WATCH_MODE_HELP_MESSAGE: &str = "在 watch 模式下可用的命令: + hint - 打印當前練習的提示 + clear - 清屏 + quit - 退出 watch 模式 + ! - 執行一個命令,例如 `!rustc --explain E0381` + help - 顯示此幫助消息 -Watch mode automatically re-evaluates the current exercise -when you edit a file's contents."; +watch 模式在您編輯文件內容時會自動重新評估當前的練習。"; diff --git a/src/project.rs b/src/project.rs index 0f56de96..6073c79d 100644 --- a/src/project.rs +++ b/src/project.rs @@ -6,8 +6,7 @@ use std::process::{Command, Stdio}; use crate::exercise::Exercise; -/// Contains the structure of resulting rust-project.json file -/// and functions to build the data required to create the file +/// 包含生成 rust-project.json 文件所需的結構和構建數據的函數 #[derive(Serialize)] struct RustAnalyzerProject { sysroot_src: PathBuf, @@ -18,10 +17,10 @@ struct RustAnalyzerProject { struct Crate { root_module: PathBuf, edition: &'static str, - // Not used, but required in the JSON file. + // 未使用,但在 JSON 文件中是必需的。 deps: Vec<()>, - // Only `test` is used for all crates. - // Therefore, an array is used instead of a `Vec`. + // 僅使用 `test` 進行所有 crates。 + // 因此,使用數組而不是 `Vec`。 cfg: [&'static str; 1], } @@ -33,7 +32,7 @@ impl RustAnalyzerProject { root_module: exercise.path, edition: "2021", deps: Vec::new(), - // This allows rust_analyzer to work inside `#[test]` blocks + // 這允許 rust_analyzer 在 `#[test]` 區塊內工作 cfg: ["test"], }) .collect(); @@ -50,13 +49,13 @@ impl RustAnalyzerProject { .arg("sysroot") .stderr(Stdio::inherit()) .output() - .context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")? + .context("無法從 `rustc` 獲取 sysroot。您是否安裝了 `rustc`?")? .stdout; let toolchain = - String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?; + String::from_utf8(toolchain).context("工具鏈路徑是無效的 UTF8")?; let toolchain = toolchain.trim_end(); - println!("Determined toolchain: {toolchain}\n"); + println!("確定的工具鏈: {toolchain}\n"); let mut sysroot_src = PathBuf::with_capacity(256); sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]); @@ -68,13 +67,13 @@ impl RustAnalyzerProject { } } -/// Write `rust-project.json` to disk. +/// 將 `rust-project.json` 寫入磁碟。 pub fn write_project_json(exercises: Vec) -> Result<()> { let content = RustAnalyzerProject::build(exercises)?; - // Using the capacity 2^14 since the file length in bytes is higher than 2^13. - // The final length is not known exactly because it depends on the user's sysroot path, - // the current number of exercises etc. + // 使用容量 2^14,因為文件長度以字節為單位高於 2^13。 + // 最終長度不確定,因為它取決於用戶的 sysroot 路徑、 + // 當前的練習數量等。 let mut buf = Vec::with_capacity(1 << 14); serde_json::to_writer(&mut buf, &content)?; std::fs::write("rust-project.json", buf)?; diff --git a/src/run.rs b/src/run.rs index 6dd0388f..a145cf00 100644 --- a/src/run.rs +++ b/src/run.rs @@ -5,10 +5,9 @@ use crate::exercise::{Exercise, Mode}; use crate::verify::test; use indicatif::ProgressBar; -// Invoke the rust compiler on the path of the given exercise, -// and run the ensuing binary. -// The verbose argument helps determine whether or not to show -// the output from the test harnesses (if the mode of the exercise is test) +// 在給定練習的路徑上調用 Rust 編譯器,並運行隨後的二進制文件。 +// verbose 參數有助於確定是否顯示 +// 測試框架的輸出(如果練習模式是測試) pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { match exercise.mode { Mode::Test => test(exercise, verbose)?, @@ -18,7 +17,7 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { Ok(()) } -// Resets the exercise by stashing the changes. +// 通過存儲更改來重置練習。 pub fn reset(exercise: &Exercise) -> Result<(), ()> { let command = Command::new("git") .arg("stash") @@ -32,12 +31,12 @@ pub fn reset(exercise: &Exercise) -> Result<(), ()> { } } -// Invoke the rust compiler on the path of the given exercise -// and run the ensuing binary. -// This is strictly for non-test binaries, so output is displayed +// 在給定練習的路徑上調用 Rust 編譯器 +// 並運行隨後的二進制文件。 +// 這僅適用於非測試二進制文件,因此顯示輸出 fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); + progress_bar.set_message(format!("正在編譯 {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation_result = exercise.compile(); @@ -46,7 +45,7 @@ fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { Err(output) => { progress_bar.finish_and_clear(); warn!( - "Compilation of {} failed!, Compiler error message:\n", + "編譯 {} 失敗!編譯器錯誤訊息:\n", exercise ); println!("{}", output.stderr); @@ -54,21 +53,21 @@ fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { } }; - progress_bar.set_message(format!("Running {exercise}...")); + progress_bar.set_message(format!("正在運行 {exercise}...")); let result = compilation.run(); progress_bar.finish_and_clear(); match result { Ok(output) => { println!("{}", output.stdout); - success!("Successfully ran {}", exercise); + success!("成功運行 {}", exercise); Ok(()) } Err(output) => { println!("{}", output.stdout); println!("{}", output.stderr); - warn!("Ran {} with errors", exercise); + warn!("運行 {} 時出現錯誤", exercise); Err(()) } } diff --git a/src/verify.rs b/src/verify.rs index dac25626..86a9ead4 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -3,11 +3,10 @@ use console::style; use indicatif::{ProgressBar, ProgressStyle}; use std::{env, time::Duration}; -// Verify that the provided container of Exercise objects -// can be compiled and run without any failures. -// Any such failures will be reported to the end user. -// If the Exercise being verified is a test, the verbose boolean -// determines whether or not the test harness outputs are displayed. +// 驗證提供的 Exercise 對象容器是否可以編譯和運行而不出現任何錯誤。 +// 任何此類錯誤都將報告給最終用戶。 +// 如果要驗證的 Exercise 是測試,則 verbose 布爾值 +// 決定是否顯示測試框架的輸出。 pub fn verify<'a>( exercises: impl IntoIterator, progress: (usize, usize), @@ -19,8 +18,8 @@ pub fn verify<'a>( let mut percentage = num_done as f32 / total as f32 * 100.0; bar.set_style( ProgressStyle::default_bar() - .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") - .expect("Progressbar template should be valid!") + .template("進度: [{bar:60.green/red}] {pos}/{len} {msg}") + .expect("進度條模板應該是有效的!") .progress_chars("#>-"), ); bar.set_position(num_done as u64); @@ -40,7 +39,7 @@ pub fn verify<'a>( bar.set_message(format!("({percentage:.1} %)")); if bar.position() == total as u64 { println!( - "Progress: You completed {} / {} exercises ({:.1} %).", + "進度: 您完成了 {} / {} 個練習 ({:.1} %)。", bar.position(), total, percentage @@ -57,16 +56,16 @@ enum RunMode { NonInteractive, } -// Compile and run the resulting test harness of the given Exercise +// 編譯並運行給定 Exercise 的測試框架 pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?; Ok(()) } -// Invoke the rust compiler without running the resulting binary +// 調用 rust 編譯器但不運行生成的二進制文件 fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); + progress_bar.set_message(format!("正在編譯 {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); let _ = compile(exercise, &progress_bar)?; @@ -75,22 +74,22 @@ fn compile_only(exercise: &Exercise, success_hints: bool) -> Result { Ok(prompt_for_completion(exercise, None, success_hints)) } -// Compile the given Exercise and run the resulting binary in an interactive mode +// 以交互模式編譯給定的 Exercise 並運行生成的二進制文件 fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {exercise}...")); + progress_bar.set_message(format!("正在編譯 {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation = compile(exercise, &progress_bar)?; - progress_bar.set_message(format!("Running {exercise}...")); + progress_bar.set_message(format!("正在運行 {exercise}...")); let result = compilation.run(); progress_bar.finish_and_clear(); let output = match result { Ok(output) => output, Err(output) => { - warn!("Ran {} with errors", exercise); + warn!("運行 {} 時出現錯誤", exercise); println!("{}", output.stdout); println!("{}", output.stderr); return Err(()); @@ -104,8 +103,8 @@ fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Re )) } -// Compile the given Exercise as a test harness and display -// the output if verbose is set to true +// 將給定的 Exercise 編譯為測試框架並顯示 +// 如果 verbose 設置為 true 則輸出 fn compile_and_test( exercise: &Exercise, run_mode: RunMode, @@ -113,7 +112,7 @@ fn compile_and_test( success_hints: bool, ) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Testing {exercise}...")); + progress_bar.set_message(format!("正在測試 {exercise}...")); progress_bar.enable_steady_tick(Duration::from_millis(100)); let compilation = compile(exercise, &progress_bar)?; @@ -133,7 +132,7 @@ fn compile_and_test( } Err(output) => { warn!( - "Testing of {} failed! Please try again. Here's the output:", + "測試 {} 失敗!請再試一次。以下是輸出:", exercise ); println!("{}", output.stdout); @@ -142,8 +141,8 @@ fn compile_and_test( } } -// Compile the given Exercise and return an object with information -// about the state of the compilation +// 編譯給定的 Exercise 並返回一個包含 +// 編譯狀態信息的對象 fn compile<'a>( exercise: &'a Exercise, progress_bar: &ProgressBar, @@ -155,7 +154,7 @@ fn compile<'a>( Err(output) => { progress_bar.finish_and_clear(); warn!( - "Compiling of {} failed! Please try again. Here's the output:", + "編譯 {} 失敗!請再試一次。以下是輸出:", exercise ); println!("{}", output.stderr); @@ -174,22 +173,22 @@ fn prompt_for_completion( State::Pending(context) => context, }; match exercise.mode { - Mode::Compile => success!("Successfully ran {}!", exercise), - Mode::Test => success!("Successfully tested {}!", exercise), - Mode::Clippy => success!("Successfully compiled {}!", exercise), + Mode::Compile => success!("成功運行 {}!", exercise), + Mode::Test => success!("成功測試 {}!", exercise), + Mode::Clippy => success!("成功編譯 {}!", exercise), } let no_emoji = env::var("NO_EMOJI").is_ok(); let clippy_success_msg = if no_emoji { - "The code is compiling, and Clippy is happy!" + "代碼正在編譯,Clippy 很滿意!" } else { - "The code is compiling, and 📎 Clippy 📎 is happy!" + "代碼正在編譯,📎 Clippy 📎 很滿意!" }; let success_msg = match exercise.mode { - Mode::Compile => "The code is compiling!", - Mode::Test => "The code is compiling, and the tests pass!", + Mode::Compile => "代碼正在編譯!", + Mode::Test => "代碼正在編譯,並且測試通過!", Mode::Clippy => clippy_success_msg, }; @@ -201,21 +200,21 @@ fn prompt_for_completion( if let Some(output) = prompt_output { println!( - "Output:\n{separator}\n{output}\n{separator}\n", + "輸出:\n{separator}\n{output}\n{separator}\n", separator = separator(), ); } if success_hints { println!( - "Hints:\n{separator}\n{}\n{separator}\n", + "提示:\n{separator}\n{}\n{separator}\n", exercise.hint, separator = separator(), ); } - println!("You can keep working on this exercise,"); + println!("您可以繼續進行此練習,"); println!( - "or jump into the next one by removing the {} comment:", + "或通過刪除 {} 註釋來進入下一個練習:", style("`I AM NOT DONE`").bold() ); println!();