Merge pull request #1151 from magnusrodseth/1031-reset-exercise

Add reset command for a given filename
This commit is contained in:
liv 2022-08-18 14:07:15 +02:00 committed by GitHub
commit 96098d228a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 20 deletions

View file

@ -1,6 +1,6 @@
use crate::exercise::{Exercise, ExerciseList}; use crate::exercise::{Exercise, ExerciseList};
use crate::project::RustAnalyzerProject; use crate::project::RustAnalyzerProject;
use crate::run::run; use crate::run::{reset, run};
use crate::verify::verify; use crate::verify::verify;
use argh::FromArgs; use argh::FromArgs;
use console::Emoji; use console::Emoji;
@ -47,6 +47,7 @@ enum Subcommands {
Verify(VerifyArgs), Verify(VerifyArgs),
Watch(WatchArgs), Watch(WatchArgs),
Run(RunArgs), Run(RunArgs),
Reset(ResetArgs),
Hint(HintArgs), Hint(HintArgs),
List(ListArgs), List(ListArgs),
Lsp(LspArgs), Lsp(LspArgs),
@ -71,6 +72,15 @@ struct RunArgs {
name: String, 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)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "hint")] #[argh(subcommand, name = "hint")]
/// Returns a hint for the given exercise /// Returns a hint for the given exercise
@ -85,7 +95,6 @@ struct HintArgs {
/// Enable rust-analyzer for exercises /// Enable rust-analyzer for exercises
struct LspArgs {} struct LspArgs {}
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "list")] #[argh(subcommand, name = "list")]
/// Lists the exercises available in Rustlings /// Lists the exercises available in Rustlings
@ -164,7 +173,9 @@ fn main() {
"Pending" "Pending"
}; };
let solve_cond = { 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()) { if solve_cond && (filter_cond || subargs.filter.is_none()) {
let line = if subargs.paths { let line = if subargs.paths {
@ -205,6 +216,12 @@ fn main() {
run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); 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) => { Subcommands::Hint(subargs) => {
let exercise = find_exercise(&subargs.name, &exercises); let exercise = find_exercise(&subargs.name, &exercises);
@ -212,7 +229,8 @@ fn main() {
} }
Subcommands::Verify(_subargs) => { 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) => { Subcommands::Lsp(_subargs) => {
@ -236,12 +254,18 @@ fn main() {
Subcommands::Watch(_subargs) => match watch(&exercises, verbose) { Subcommands::Watch(_subargs) => match watch(&exercises, verbose) {
Err(e) => { 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."); println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
std::process::exit(1); std::process::exit(1);
} }
Ok(WatchStatus::Finished) => { Ok(WatchStatus::Finished) => {
println!("{emoji} All exercises completed! {emoji}", emoji = Emoji("🎉", "")); println!(
"{emoji} All exercises completed! {emoji}",
emoji = Emoji("🎉", "")
);
println!("\n{}\n", FENISH_LINE); println!("\n{}\n", FENISH_LINE);
} }
Ok(WatchStatus::Unfinished) => { Ok(WatchStatus::Unfinished) => {
@ -252,8 +276,10 @@ fn main() {
} }
} }
fn spawn_watch_shell(
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_quit: Arc<AtomicBool>) { failed_exercise_hint: &Arc<Mutex<Option<String>>>,
should_quit: Arc<AtomicBool>,
) {
let failed_exercise_hint = Arc::clone(failed_exercise_hint); 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."); println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
thread::spawn(move || loop { thread::spawn(move || loop {
@ -290,16 +316,22 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_q
fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
if name.eq("next") { if name.eq("next") {
exercises.iter().find(|e| !e.looks_done()).unwrap_or_else(|| { exercises
println!("🎉 Congratulations! You have done all the exercises!"); .iter()
println!("🔚 There are no more exercises to do next!"); .find(|e| !e.looks_done())
std::process::exit(1) .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 { } else {
exercises.iter().find(|e| e.name == name).unwrap_or_else(|| { exercises
println!("No exercise found for '{}'!", name); .iter()
std::process::exit(1) .find(|e| e.name == name)
}) .unwrap_or_else(|| {
println!("No exercise found for '{}'!", name);
std::process::exit(1)
})
} }
} }
@ -337,8 +369,13 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
let filepath = b.as_path().canonicalize().unwrap(); let filepath = b.as_path().canonicalize().unwrap();
let pending_exercises = exercises let pending_exercises = exercises
.iter() .iter()
.find(|e| filepath.ends_with(&e.path)).into_iter() .find(|e| filepath.ends_with(&e.path))
.chain(exercises.iter().filter(|e| !e.looks_done() && !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(); let num_done = exercises.iter().filter(|e| e.looks_done()).count();
clear_screen(); clear_screen();
match verify(pending_exercises, (num_done, exercises.len()), verbose) { match verify(pending_exercises, (num_done, exercises.len()), verbose) {

View file

@ -1,3 +1,5 @@
use std::process::Command;
use crate::exercise::{Exercise, Mode}; use crate::exercise::{Exercise, Mode};
use crate::verify::test; use crate::verify::test;
use indicatif::ProgressBar; use indicatif::ProgressBar;
@ -15,6 +17,19 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
Ok(()) 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 // Invoke the rust compiler on the path of the given exercise
// and run the ensuing binary. // and run the ensuing binary.
// This is strictly for non-test binaries, so output is displayed // This is strictly for non-test binaries, so output is displayed

View file

@ -110,6 +110,27 @@ fn run_single_test_no_exercise() {
.code(1); .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] #[test]
fn get_hint_for_single_test() { fn get_hint_for_single_test() {
Command::cargo_bin("rustlings") Command::cargo_bin("rustlings")
@ -126,7 +147,7 @@ fn all_exercises_require_confirmation() {
for exercise in glob("exercises/**/*.rs").unwrap() { for exercise in glob("exercises/**/*.rs").unwrap() {
let path = exercise.unwrap(); let path = exercise.unwrap();
if path.file_name().unwrap() == "mod.rs" { if path.file_name().unwrap() == "mod.rs" {
continue continue;
} }
let source = { let source = {
let mut file = File::open(&path).unwrap(); let mut file = File::open(&path).unwrap();