summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/exercise.rs18
-rw-r--r--src/main.rs207
-rw-r--r--src/project.rs2
-rw-r--r--src/run.rs3
-rw-r--r--src/verify.rs39
5 files changed, 125 insertions, 144 deletions
diff --git a/src/exercise.rs b/src/exercise.rs
index 2cde4e1..07251db 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -110,12 +110,12 @@ impl Exercise {
pub fn compile(&self) -> Result<CompiledExercise, ExerciseOutput> {
let cmd = match self.mode {
Mode::Compile => Command::new("rustc")
- .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
+ .args([self.path.to_str().unwrap(), "-o", &temp_file()])
.args(RUSTC_COLOR_ARGS)
.args(RUSTC_EDITION_ARGS)
.output(),
Mode::Test => Command::new("rustc")
- .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
+ .args(["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
.args(RUSTC_COLOR_ARGS)
.args(RUSTC_EDITION_ARGS)
.output(),
@@ -141,7 +141,7 @@ path = "{}.rs""#,
// compilation failure, this would silently fail. But we expect
// clippy to reflect the same failure while compiling later.
Command::new("rustc")
- .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
+ .args([self.path.to_str().unwrap(), "-o", &temp_file()])
.args(RUSTC_COLOR_ARGS)
.args(RUSTC_EDITION_ARGS)
.output()
@@ -151,14 +151,14 @@ path = "{}.rs""#,
// This is already fixed on Clippy's master branch. See this issue to track merging into Cargo:
// https://github.com/rust-lang/rust-clippy/issues/3837
Command::new("cargo")
- .args(&["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
+ .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
.args(RUSTC_COLOR_ARGS)
.output()
.expect("Failed to run 'cargo clean'");
Command::new("cargo")
- .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
+ .args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
.args(RUSTC_COLOR_ARGS)
- .args(&["--", "-D", "warnings", "-D", "clippy::float_cmp"])
+ .args(["--", "-D", "warnings", "-D", "clippy::float_cmp"])
.output()
}
}
@@ -183,7 +183,7 @@ path = "{}.rs""#,
Mode::Test => "--show-output",
_ => "",
};
- let cmd = Command::new(&temp_file())
+ let cmd = Command::new(temp_file())
.arg(arg)
.output()
.expect("Failed to run 'run' command");
@@ -260,7 +260,7 @@ impl Display for Exercise {
#[inline]
fn clean() {
- let _ignored = remove_file(&temp_file());
+ let _ignored = remove_file(temp_file());
}
#[cfg(test)]
@@ -270,7 +270,7 @@ mod test {
#[test]
fn test_clean() {
- File::create(&temp_file()).unwrap();
+ File::create(temp_file()).unwrap();
let exercise = Exercise {
name: String::from("example"),
path: PathBuf::from("tests/fixture/state/pending_exercise.rs"),
diff --git a/src/main.rs b/src/main.rs
index 0a9af2e..a4b764d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,7 +2,7 @@ use crate::exercise::{Exercise, ExerciseList};
use crate::project::RustAnalyzerProject;
use crate::run::{reset, run};
use crate::verify::verify;
-use argh::FromArgs;
+use clap::{Parser, Subcommand};
use console::Emoji;
use notify::DebouncedEvent;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
@@ -25,111 +25,69 @@ mod project;
mod run;
mod verify;
-// In sync with crate version
-const VERSION: &str = "5.5.1";
-
-#[derive(FromArgs, PartialEq, Debug)]
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
+#[derive(Parser)]
+#[command(version)]
struct Args {
- /// show outputs from the test exercises
- #[argh(switch)]
+ /// Show outputs from the test exercises
+ #[arg(long)]
nocapture: bool,
- /// show the executable version
- #[argh(switch, short = 'v')]
- version: bool,
- #[argh(subcommand)]
- nested: Option<Subcommands>,
+ #[command(subcommand)]
+ command: Option<Subcommands>,
}
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand)]
+#[derive(Subcommand)]
enum Subcommands {
- Verify(VerifyArgs),
- Watch(WatchArgs),
- Run(RunArgs),
- Reset(ResetArgs),
- Hint(HintArgs),
- List(ListArgs),
- Lsp(LspArgs),
-}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "verify")]
-/// Verifies all exercises according to the recommended order
-struct VerifyArgs {}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "watch")]
-/// Reruns `verify` when files were edited
-struct WatchArgs {
- /// show hints on success
- #[argh(switch)]
- success_hints: bool,
-}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "run")]
-/// Runs/Tests a single exercise
-struct RunArgs {
- #[argh(positional)]
- /// the name of the exercise
- name: String,
-}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "reset")]
-/// Resets a single exercise using "git stash -- <filename>"
-struct ResetArgs {
- #[argh(positional)]
- /// the name of the exercise
- name: String,
-}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "hint")]
-/// Returns a hint for the given exercise
-struct HintArgs {
- #[argh(positional)]
- /// the name of the exercise
- name: String,
-}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "lsp")]
-/// Enable rust-analyzer for exercises
-struct LspArgs {}
-
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "list")]
-/// Lists the exercises available in Rustlings
-struct ListArgs {
- #[argh(switch, short = 'p')]
- /// show only the paths of the exercises
- paths: bool,
- #[argh(switch, short = 'n')]
- /// show only the names of the exercises
- names: bool,
- #[argh(option, short = 'f')]
- /// provide a string to match exercise names
- /// comma separated patterns are acceptable
- filter: Option<String>,
- #[argh(switch, short = 'u')]
- /// display only exercises not yet solved
- unsolved: bool,
- #[argh(switch, short = 's')]
- /// display only exercises that have been solved
- solved: bool,
+ /// Verify all exercises according to the recommended order
+ Verify,
+ /// Rerun `verify` when files were edited
+ 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>"
+ 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
+ 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
+ Lsp,
}
fn main() {
- let args: Args = argh::from_env();
+ let args = Args::parse();
- if args.version {
- println!("v{VERSION}");
- std::process::exit(0);
- }
-
- if args.nested.is_none() {
+ if args.command.is_none() {
println!("\n{WELCOME}\n");
}
@@ -153,23 +111,30 @@ fn main() {
let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;
let verbose = args.nocapture;
- let command = args.nested.unwrap_or_else(|| {
+ let command = args.command.unwrap_or_else(|| {
println!("{DEFAULT_OUT}\n");
std::process::exit(0);
});
+
match command {
- Subcommands::List(subargs) => {
- if !subargs.paths && !subargs.names {
+ Subcommands::List {
+ paths,
+ names,
+ filter,
+ unsolved,
+ solved,
+ } => {
+ if !paths && !names {
println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status");
}
let mut exercises_done: u16 = 0;
- let filters = subargs.filter.clone().unwrap_or_default().to_lowercase();
+ let filters = filter.clone().unwrap_or_default().to_lowercase();
exercises.iter().for_each(|e| {
let fname = format!("{}", e.path.display());
let filter_cond = filters
.split(',')
.filter(|f| !f.trim().is_empty())
- .any(|f| e.name.contains(&f) || fname.contains(&f));
+ .any(|f| e.name.contains(f) || fname.contains(f));
let status = if e.looks_done() {
exercises_done += 1;
"Done"
@@ -177,14 +142,14 @@ fn main() {
"Pending"
};
let solve_cond = {
- (e.looks_done() && subargs.solved)
- || (!e.looks_done() && subargs.unsolved)
- || (!subargs.solved && !subargs.unsolved)
+ (e.looks_done() && solved)
+ || (!e.looks_done() && unsolved)
+ || (!solved && !unsolved)
};
- if solve_cond && (filter_cond || subargs.filter.is_none()) {
- let line = if subargs.paths {
+ if solve_cond && (filter_cond || filter.is_none()) {
+ let line = if paths {
format!("{fname}\n")
- } else if subargs.names {
+ } else if names {
format!("{}\n", e.name)
} else {
format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name)
@@ -214,30 +179,30 @@ fn main() {
std::process::exit(0);
}
- Subcommands::Run(subargs) => {
- let exercise = find_exercise(&subargs.name, &exercises);
+ Subcommands::Run { name } => {
+ let exercise = find_exercise(&name, &exercises);
run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
}
- Subcommands::Reset(subargs) => {
- let exercise = find_exercise(&subargs.name, &exercises);
+ Subcommands::Reset { name } => {
+ let exercise = find_exercise(&name, &exercises);
reset(exercise).unwrap_or_else(|_| std::process::exit(1));
}
- Subcommands::Hint(subargs) => {
- let exercise = find_exercise(&subargs.name, &exercises);
+ Subcommands::Hint { name } => {
+ let exercise = find_exercise(&name, &exercises);
println!("{}", exercise.hint);
}
- Subcommands::Verify(_subargs) => {
+ Subcommands::Verify => {
verify(&exercises, (0, exercises.len()), verbose, false)
.unwrap_or_else(|_| std::process::exit(1));
}
- Subcommands::Lsp(_subargs) => {
+ Subcommands::Lsp => {
let mut project = RustAnalyzerProject::new();
project
.get_sysroot_src()
@@ -256,7 +221,7 @@ fn main() {
}
}
- Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) {
+ Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) {
Err(e) => {
println!(
"Error: Could not watch your progress. Error message was {:?}.",
@@ -429,7 +394,7 @@ fn watch(
fn rustc_exists() -> bool {
Command::new("rustc")
- .args(&["--version"])
+ .args(["--version"])
.stdout(Stdio::null())
.spawn()
.and_then(|mut child| child.wait())
@@ -465,7 +430,7 @@ started, here's a couple of notes about how Rustlings operates:
Got all that? Great! To get started, run `rustlings watch` in order to get the first
exercise. Make sure to have your editor open!"#;
-const FENISH_LINE: &str = r#"+----------------------------------------------------+
+const FENISH_LINE: &str = r"+----------------------------------------------------+
| You made it to the Fe-nish line! |
+-------------------------- ------------------------+
\\/
@@ -490,12 +455,12 @@ 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!
Before reporting an issue or contributing, please read our guidelines:
-https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"#;
+https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md";
-const WELCOME: &str = r#" welcome to...
+const WELCOME: &str = r" welcome to...
_ _ _
_ __ _ _ ___| |_| (_)_ __ __ _ ___
| '__| | | / __| __| | | '_ \ / _` / __|
| | | |_| \__ \ |_| | | | | | (_| \__ \
|_| \__,_|___/\__|_|_|_| |_|\__, |___/
- |___/"#;
+ |___/";
diff --git a/src/project.rs b/src/project.rs
index ebebe27..bcbd7ad 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -86,7 +86,7 @@ impl RustAnalyzerProject {
println!("Determined toolchain: {}\n", &toolchain);
- self.sysroot_src = (std::path::Path::new(&*toolchain)
+ self.sysroot_src = (std::path::Path::new(toolchain)
.join("lib")
.join("rustlib")
.join("src")
diff --git a/src/run.rs b/src/run.rs
index 1e2e56c..e0ada4c 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -1,4 +1,5 @@
use std::process::Command;
+use std::time::Duration;
use crate::exercise::{Exercise, Mode};
use crate::verify::test;
@@ -36,7 +37,7 @@ pub fn reset(exercise: &Exercise) -> Result<(), ()> {
fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {exercise}..."));
- progress_bar.enable_steady_tick(100);
+ progress_bar.enable_steady_tick(Duration::from_millis(100));
let compilation_result = exercise.compile();
let compilation = match compilation_result {
diff --git a/src/verify.rs b/src/verify.rs
index f3f3b56..8a2ad49 100644
--- a/src/verify.rs
+++ b/src/verify.rs
@@ -1,7 +1,7 @@
use crate::exercise::{CompiledExercise, Exercise, Mode, State};
use console::style;
use indicatif::{ProgressBar, ProgressStyle};
-use std::env;
+use std::{env, time::Duration};
// Verify that the provided container of Exercise objects
// can be compiled and run without any failures.
@@ -17,9 +17,11 @@ pub fn verify<'a>(
let (num_done, total) = progress;
let bar = ProgressBar::new(total as u64);
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}")
- .progress_chars("#>-")
+ bar.set_style(
+ ProgressStyle::default_bar()
+ .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}")
+ .expect("Progressbar template should be valid!")
+ .progress_chars("#>-"),
);
bar.set_position(num_done as u64);
bar.set_message(format!("({:.1} %)", percentage));
@@ -55,7 +57,7 @@ pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
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.enable_steady_tick(100);
+ progress_bar.enable_steady_tick(Duration::from_millis(100));
let _ = compile(exercise, &progress_bar)?;
progress_bar.finish_and_clear();
@@ -67,7 +69,7 @@ fn compile_only(exercise: &Exercise, success_hints: bool) -> Result<bool, ()> {
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.enable_steady_tick(100);
+ progress_bar.enable_steady_tick(Duration::from_millis(100));
let compilation = compile(exercise, &progress_bar)?;
@@ -85,15 +87,24 @@ fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Re
}
};
- Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints))
+ Ok(prompt_for_completion(
+ exercise,
+ Some(output.stdout),
+ success_hints,
+ ))
}
// Compile the given Exercise as a test harness and display
// the output if verbose is set to true
-fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result<bool, ()> {
+fn compile_and_test(
+ exercise: &Exercise,
+ run_mode: RunMode,
+ verbose: bool,
+ success_hints: bool,
+) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Testing {exercise}..."));
- progress_bar.enable_steady_tick(100);
+ progress_bar.enable_steady_tick(Duration::from_millis(100));
let compilation = compile(exercise, &progress_bar)?;
let result = compilation.run();
@@ -123,9 +134,9 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, succe
// Compile the given Exercise and return an object with information
// about the state of the compilation
-fn compile<'a, 'b>(
+fn compile<'a>(
exercise: &'a Exercise,
- progress_bar: &'b ProgressBar,
+ progress_bar: &ProgressBar,
) -> Result<CompiledExercise<'a>, ()> {
let compilation_result = exercise.compile();
@@ -143,7 +154,11 @@ fn compile<'a, 'b>(
}
}
-fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>, success_hints: bool) -> bool {
+fn prompt_for_completion(
+ exercise: &Exercise,
+ prompt_output: Option<String>,
+ success_hints: bool,
+) -> bool {
let context = match exercise.state() {
State::Done => return true,
State::Pending(context) => context,