mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-28 06:49:19 +00:00
翻譯
This commit is contained in:
parent
235b3bdf50
commit
44fc93af91
104
src/exercise.rs
104
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<Exercise>,
|
||||
}
|
||||
|
||||
// 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<ContextLine>),
|
||||
}
|
||||
|
||||
// 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<ExerciseOutput, ExerciseOutput> {
|
||||
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(""),
|
||||
|
||||
167
src/main.rs
167
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 -- <filename>"
|
||||
/// 使用 "git stash -- <filename>" 重置單個練習
|
||||
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<String>,
|
||||
/// 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<Mutex<Option<String>>>,
|
||||
should_quit: Arc<AtomicBool>,
|
||||
) {
|
||||
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<WatchStatus> {
|
||||
/* 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
|
||||
!<cmd> - executes a command, like `!rustc --explain E0381`
|
||||
help - displays this help message
|
||||
const WATCH_MODE_HELP_MESSAGE: &str = "在 watch 模式下可用的命令:
|
||||
hint - 打印當前練習的提示
|
||||
clear - 清屏
|
||||
quit - 退出 watch 模式
|
||||
!<cmd> - 執行一個命令,例如 `!rustc --explain E0381`
|
||||
help - 顯示此幫助消息
|
||||
|
||||
Watch mode automatically re-evaluates the current exercise
|
||||
when you edit a file's contents.";
|
||||
watch 模式在您編輯文件內容時會自動重新評估當前的練習。";
|
||||
|
||||
@ -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<Exercise>) -> 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)?;
|
||||
|
||||
25
src/run.rs
25
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Item = &'a Exercise>,
|
||||
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<bool, ()> {
|
||||
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<bool, ()> {
|
||||
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<bool, ()> {
|
||||
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<bool, ()> {
|
||||
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!();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user