Skip to content

Commit 96098d2

Browse files
authored
Merge pull request #1151 from magnusrodseth/1031-reset-exercise
Add reset command for a given filename
2 parents fb7d3bf + d59dde3 commit 96098d2

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

src/main.rs

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::exercise::{Exercise, ExerciseList};
22
use crate::project::RustAnalyzerProject;
3-
use crate::run::run;
3+
use crate::run::{reset, run};
44
use crate::verify::verify;
55
use argh::FromArgs;
66
use console::Emoji;
@@ -47,6 +47,7 @@ enum Subcommands {
4747
Verify(VerifyArgs),
4848
Watch(WatchArgs),
4949
Run(RunArgs),
50+
Reset(ResetArgs),
5051
Hint(HintArgs),
5152
List(ListArgs),
5253
Lsp(LspArgs),
@@ -71,6 +72,15 @@ struct RunArgs {
7172
name: String,
7273
}
7374

75+
#[derive(FromArgs, PartialEq, Debug)]
76+
#[argh(subcommand, name = "reset")]
77+
/// Resets a single exercise using "git stash -- <filename>"
78+
struct ResetArgs {
79+
#[argh(positional)]
80+
/// the name of the exercise
81+
name: String,
82+
}
83+
7484
#[derive(FromArgs, PartialEq, Debug)]
7585
#[argh(subcommand, name = "hint")]
7686
/// Returns a hint for the given exercise
@@ -85,7 +95,6 @@ struct HintArgs {
8595
/// Enable rust-analyzer for exercises
8696
struct LspArgs {}
8797

88-
8998
#[derive(FromArgs, PartialEq, Debug)]
9099
#[argh(subcommand, name = "list")]
91100
/// Lists the exercises available in Rustlings
@@ -164,7 +173,9 @@ fn main() {
164173
"Pending"
165174
};
166175
let solve_cond = {
167-
(e.looks_done() && subargs.solved) || (!e.looks_done() && subargs.unsolved) || (!subargs.solved && !subargs.unsolved)
176+
(e.looks_done() && subargs.solved)
177+
|| (!e.looks_done() && subargs.unsolved)
178+
|| (!subargs.solved && !subargs.unsolved)
168179
};
169180
if solve_cond && (filter_cond || subargs.filter.is_none()) {
170181
let line = if subargs.paths {
@@ -205,14 +216,21 @@ fn main() {
205216
run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1));
206217
}
207218

219+
Subcommands::Reset(subargs) => {
220+
let exercise = find_exercise(&subargs.name, &exercises);
221+
222+
reset(exercise).unwrap_or_else(|_| std::process::exit(1));
223+
}
224+
208225
Subcommands::Hint(subargs) => {
209226
let exercise = find_exercise(&subargs.name, &exercises);
210227

211228
println!("{}", exercise.hint);
212229
}
213230

214231
Subcommands::Verify(_subargs) => {
215-
verify(&exercises, (0, exercises.len()), verbose).unwrap_or_else(|_| std::process::exit(1));
232+
verify(&exercises, (0, exercises.len()), verbose)
233+
.unwrap_or_else(|_| std::process::exit(1));
216234
}
217235

218236
Subcommands::Lsp(_subargs) => {
@@ -236,12 +254,18 @@ fn main() {
236254

237255
Subcommands::Watch(_subargs) => match watch(&exercises, verbose) {
238256
Err(e) => {
239-
println!("Error: Could not watch your progress. Error message was {:?}.", e);
257+
println!(
258+
"Error: Could not watch your progress. Error message was {:?}.",
259+
e
260+
);
240261
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
241262
std::process::exit(1);
242263
}
243264
Ok(WatchStatus::Finished) => {
244-
println!("{emoji} All exercises completed! {emoji}", emoji = Emoji("🎉", "★"));
265+
println!(
266+
"{emoji} All exercises completed! {emoji}",
267+
emoji = Emoji("🎉", "★")
268+
);
245269
println!("\n{}\n", FENISH_LINE);
246270
}
247271
Ok(WatchStatus::Unfinished) => {
@@ -252,8 +276,10 @@ fn main() {
252276
}
253277
}
254278

255-
256-
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_quit: Arc<AtomicBool>) {
279+
fn spawn_watch_shell(
280+
failed_exercise_hint: &Arc<Mutex<Option<String>>>,
281+
should_quit: Arc<AtomicBool>,
282+
) {
257283
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
258284
println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
259285
thread::spawn(move || loop {
@@ -290,16 +316,22 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_q
290316

291317
fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
292318
if name.eq("next") {
293-
exercises.iter().find(|e| !e.looks_done()).unwrap_or_else(|| {
294-
println!("🎉 Congratulations! You have done all the exercises!");
295-
println!("🔚 There are no more exercises to do next!");
296-
std::process::exit(1)
297-
})
319+
exercises
320+
.iter()
321+
.find(|e| !e.looks_done())
322+
.unwrap_or_else(|| {
323+
println!("🎉 Congratulations! You have done all the exercises!");
324+
println!("🔚 There are no more exercises to do next!");
325+
std::process::exit(1)
326+
})
298327
} else {
299-
exercises.iter().find(|e| e.name == name).unwrap_or_else(|| {
300-
println!("No exercise found for '{}'!", name);
301-
std::process::exit(1)
302-
})
328+
exercises
329+
.iter()
330+
.find(|e| e.name == name)
331+
.unwrap_or_else(|| {
332+
println!("No exercise found for '{}'!", name);
333+
std::process::exit(1)
334+
})
303335
}
304336
}
305337

@@ -337,8 +369,13 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
337369
let filepath = b.as_path().canonicalize().unwrap();
338370
let pending_exercises = exercises
339371
.iter()
340-
.find(|e| filepath.ends_with(&e.path)).into_iter()
341-
.chain(exercises.iter().filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)));
372+
.find(|e| filepath.ends_with(&e.path))
373+
.into_iter()
374+
.chain(
375+
exercises
376+
.iter()
377+
.filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)),
378+
);
342379
let num_done = exercises.iter().filter(|e| e.looks_done()).count();
343380
clear_screen();
344381
match verify(pending_exercises, (num_done, exercises.len()), verbose) {

src/run.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::process::Command;
2+
13
use crate::exercise::{Exercise, Mode};
24
use crate::verify::test;
35
use indicatif::ProgressBar;
@@ -15,6 +17,19 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
1517
Ok(())
1618
}
1719

20+
// Resets the exercise by stashing the changes.
21+
pub fn reset(exercise: &Exercise) -> Result<(), ()> {
22+
let command = Command::new("git")
23+
.args(["stash", "--"])
24+
.arg(&exercise.path)
25+
.spawn();
26+
27+
match command {
28+
Ok(_) => Ok(()),
29+
Err(_) => Err(()),
30+
}
31+
}
32+
1833
// Invoke the rust compiler on the path of the given exercise
1934
// and run the ensuing binary.
2035
// This is strictly for non-test binaries, so output is displayed

tests/integration_tests.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ fn run_single_test_no_exercise() {
110110
.code(1);
111111
}
112112

113+
#[test]
114+
fn reset_single_exercise() {
115+
Command::cargo_bin("rustlings")
116+
.unwrap()
117+
.args(&["reset", "intro1"])
118+
.assert()
119+
.code(0);
120+
}
121+
122+
#[test]
123+
fn reset_no_exercise() {
124+
Command::cargo_bin("rustlings")
125+
.unwrap()
126+
.arg("reset")
127+
.assert()
128+
.code(1)
129+
.stderr(predicates::str::contains(
130+
"positional arguments not provided",
131+
));
132+
}
133+
113134
#[test]
114135
fn get_hint_for_single_test() {
115136
Command::cargo_bin("rustlings")
@@ -126,7 +147,7 @@ fn all_exercises_require_confirmation() {
126147
for exercise in glob("exercises/**/*.rs").unwrap() {
127148
let path = exercise.unwrap();
128149
if path.file_name().unwrap() == "mod.rs" {
129-
continue
150+
continue;
130151
}
131152
let source = {
132153
let mut file = File::open(&path).unwrap();

0 commit comments

Comments
 (0)