Merge pull request #1151 from magnusrodseth/1031-reset-exercise
Add reset command for a given filename
This commit is contained in:
commit
96098d228a
61
src/main.rs
61
src/main.rs
|
@ -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,13 +316,19 @@ 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
|
||||||
|
.iter()
|
||||||
|
.find(|e| !e.looks_done())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
println!("🎉 Congratulations! You have done all the exercises!");
|
println!("🎉 Congratulations! You have done all the exercises!");
|
||||||
println!("🔚 There are no more exercises to do next!");
|
println!("🔚 There are no more exercises to do next!");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
exercises.iter().find(|e| e.name == name).unwrap_or_else(|| {
|
exercises
|
||||||
|
.iter()
|
||||||
|
.find(|e| e.name == name)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
println!("No exercise found for '{}'!", name);
|
println!("No exercise found for '{}'!", name);
|
||||||
std::process::exit(1)
|
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) {
|
||||||
|
|
15
src/run.rs
15
src/run.rs
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue