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