From 6f44cb1dd237f4d53aa3ecd4676e50cf850c99fe Mon Sep 17 00:00:00 2001 From: magnusrodseth <59113973+magnusrodseth@users.noreply.github.com> Date: Wed, 17 Aug 2022 16:31:53 +0200 Subject: [PATCH 1/3] feat: Add reset command, given a filename --- src/main.rs | 78 ++++++++++++++++++++++++++++++++++++++++------------- src/run.rs | 15 +++++++++++ 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index 531533e..3610f82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ -use crate::exercise::{Exercise, ExerciseList}; use crate::project::RustAnalyzerProject; use crate::run::run; use crate::verify::verify; +use crate::{ + exercise::{Exercise, ExerciseList}, + run::reset, +}; use argh::FromArgs; use console::Emoji; use notify::DebouncedEvent; @@ -47,6 +50,7 @@ enum Subcommands { Verify(VerifyArgs), Watch(WatchArgs), Run(RunArgs), + Reset(ResetArgs), Hint(HintArgs), List(ListArgs), Lsp(LspArgs), @@ -71,6 +75,15 @@ struct RunArgs { name: String, } +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "reset")] +/// Resets a single exercise +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 @@ -85,7 +98,6 @@ struct HintArgs { /// Enable rust-analyzer for exercises struct LspArgs {} - #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "list")] /// Lists the exercises available in Rustlings @@ -164,7 +176,9 @@ fn main() { "Pending" }; let solve_cond = { - (e.looks_done() && subargs.solved) || (!e.looks_done() && subargs.unsolved) || (!subargs.solved && !subargs.unsolved) + (e.looks_done() && subargs.solved) + || (!e.looks_done() && subargs.unsolved) + || (!subargs.solved && !subargs.unsolved) }; if solve_cond && (filter_cond || subargs.filter.is_none()) { let line = if subargs.paths { @@ -205,6 +219,12 @@ fn main() { run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); } + Subcommands::Reset(subargs) => { + let exercise = find_exercise(&subargs.name, &exercises); + + reset(exercise).unwrap_or_else(|_| std::process::exit(1)); + } + Subcommands::Hint(subargs) => { let exercise = find_exercise(&subargs.name, &exercises); @@ -212,7 +232,8 @@ fn main() { } Subcommands::Verify(_subargs) => { - verify(&exercises, (0, exercises.len()), verbose).unwrap_or_else(|_| std::process::exit(1)); + verify(&exercises, (0, exercises.len()), verbose) + .unwrap_or_else(|_| std::process::exit(1)); } Subcommands::Lsp(_subargs) => { @@ -236,12 +257,18 @@ fn main() { Subcommands::Watch(_subargs) => match watch(&exercises, verbose) { Err(e) => { - println!("Error: Could not watch your progress. Error message was {:?}.", 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."); std::process::exit(1); } Ok(WatchStatus::Finished) => { - println!("{emoji} All exercises completed! {emoji}", emoji = Emoji("🎉", "★")); + println!( + "{emoji} All exercises completed! {emoji}", + emoji = Emoji("🎉", "★") + ); println!("\n{}\n", FENISH_LINE); } Ok(WatchStatus::Unfinished) => { @@ -252,8 +279,10 @@ fn main() { } } - -fn spawn_watch_shell(failed_exercise_hint: &Arc>>, should_quit: Arc) { +fn spawn_watch_shell( + failed_exercise_hint: &Arc>>, + should_quit: Arc, +) { let failed_exercise_hint = Arc::clone(failed_exercise_hint); println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); thread::spawn(move || loop { @@ -290,16 +319,22 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc>>, should_q fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { if name.eq("next") { - exercises.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!"); - std::process::exit(1) - }) + exercises + .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!"); + std::process::exit(1) + }) } else { - exercises.iter().find(|e| e.name == name).unwrap_or_else(|| { - println!("No exercise found for '{}'!", name); - std::process::exit(1) - }) + exercises + .iter() + .find(|e| e.name == name) + .unwrap_or_else(|| { + println!("No exercise found for '{}'!", name); + std::process::exit(1) + }) } } @@ -337,8 +372,13 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { let filepath = b.as_path().canonicalize().unwrap(); let pending_exercises = exercises .iter() - .find(|e| filepath.ends_with(&e.path)).into_iter() - .chain(exercises.iter().filter(|e| !e.looks_done() && !filepath.ends_with(&e.path))); + .find(|e| filepath.ends_with(&e.path)) + .into_iter() + .chain( + exercises + .iter() + .filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)), + ); let num_done = exercises.iter().filter(|e| e.looks_done()).count(); clear_screen(); match verify(pending_exercises, (num_done, exercises.len()), verbose) { diff --git a/src/run.rs b/src/run.rs index eb55885..826f00a 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,3 +1,5 @@ +use std::process::Command; + use crate::exercise::{Exercise, Mode}; use crate::verify::test; use indicatif::ProgressBar; @@ -15,6 +17,19 @@ 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") + .args(["stash", "--"]) + .arg(&exercise.path) + .spawn(); + + match command { + Ok(_) => Ok(()), + Err(_) => Err(()), + } +} + // 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 From 0aff5340b5e225b394ac0fdf496824eaa201e19c Mon Sep 17 00:00:00 2001 From: magnusrodseth <59113973+magnusrodseth@users.noreply.github.com> Date: Wed, 17 Aug 2022 16:43:48 +0200 Subject: [PATCH 2/3] test: Add integration tests --- tests/integration_tests.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 0be191f..1a72923 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -110,6 +110,27 @@ fn run_single_test_no_exercise() { .code(1); } +#[test] +fn reset_single_exercise() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["reset", "intro1"]) + .assert() + .code(0); +} + +#[test] +fn reset_no_exercise() { + Command::cargo_bin("rustlings") + .unwrap() + .arg("reset") + .assert() + .code(1) + .stderr(predicates::str::contains( + "positional arguments not provided", + )); +} + #[test] fn get_hint_for_single_test() { Command::cargo_bin("rustlings") @@ -126,7 +147,7 @@ fn all_exercises_require_confirmation() { for exercise in glob("exercises/**/*.rs").unwrap() { let path = exercise.unwrap(); if path.file_name().unwrap() == "mod.rs" { - continue + continue; } let source = { let mut file = File::open(&path).unwrap(); From d59dde320b90ac5f5679a8f044746e37faa7289f Mon Sep 17 00:00:00 2001 From: magnusrodseth <59113973+magnusrodseth@users.noreply.github.com> Date: Thu, 18 Aug 2022 12:47:26 +0200 Subject: [PATCH 3/3] chore: Add suggested changes --- src/main.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3610f82..9bf75e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,7 @@ +use crate::exercise::{Exercise, ExerciseList}; use crate::project::RustAnalyzerProject; -use crate::run::run; +use crate::run::{reset, run}; use crate::verify::verify; -use crate::{ - exercise::{Exercise, ExerciseList}, - run::reset, -}; use argh::FromArgs; use console::Emoji; use notify::DebouncedEvent; @@ -77,7 +74,7 @@ struct RunArgs { #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "reset")] -/// Resets a single exercise +/// Resets a single exercise using "git stash -- " struct ResetArgs { #[argh(positional)] /// the name of the exercise