Skip to content

Commit 1caef0b

Browse files
committed
feat: Add "quit" command to rustlings watch
closes: #842
1 parent ae56cba commit 1caef0b

File tree

1 file changed

+72
-50
lines changed

1 file changed

+72
-50
lines changed

src/main.rs

Lines changed: 72 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use std::fs;
1010
use std::io::{self, prelude::*};
1111
use std::path::Path;
1212
use std::process::{Command, Stdio};
13-
use std::sync::mpsc::channel;
13+
use std::sync::atomic::{AtomicBool, Ordering};
14+
use std::sync::mpsc::{RecvTimeoutError, channel};
1415
use std::sync::{Arc, Mutex};
1516
use std::thread;
1617
use std::time::Duration;
@@ -217,68 +218,76 @@ fn main() {
217218
}
218219

219220
Subcommands::Watch(_subargs) => {
220-
if let Err(e) = watch(&exercises, verbose) {
221-
println!(
222-
"Error: Could not watch your progress. Error message was {:?}.",
223-
e
224-
);
225-
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
226-
std::process::exit(1);
221+
match watch(&exercises, verbose) {
222+
Err(e) => {
223+
println!(
224+
"Error: Could not watch your progress. Error message was {:?}.",
225+
e
226+
);
227+
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
228+
std::process::exit(1);
229+
}
230+
Ok(WatchStatus::Finished) => {
231+
println!(
232+
"{emoji} All exercises completed! {emoji}",
233+
emoji = Emoji("🎉", "★")
234+
);
235+
println!();
236+
println!("+----------------------------------------------------+");
237+
println!("| You made it to the Fe-nish line! |");
238+
println!("+-------------------------- ------------------------+");
239+
println!(" \\/ ");
240+
println!(" ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ");
241+
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
242+
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
243+
println!(" ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ ");
244+
println!(" ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ ");
245+
println!(" ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ ");
246+
println!(" ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ ");
247+
println!(" ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ ");
248+
println!(" ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ");
249+
println!(" ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ ");
250+
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
251+
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
252+
println!(" ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ ");
253+
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ");
254+
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ");
255+
println!();
256+
println!("We hope you enjoyed learning about the various aspects of Rust!");
257+
println!(
258+
"If you noticed any issues, please don't hesitate to report them to our repo."
259+
);
260+
println!("You can also contribute your own exercises to help the greater community!");
261+
println!();
262+
println!("Before reporting an issue or contributing, please read our guidelines:");
263+
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
264+
}
265+
Ok(WatchStatus::Unfinished) => {
266+
println!("We hope you're enjoying learning about Rust!");
267+
println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again");
268+
}
227269
}
228-
println!(
229-
"{emoji} All exercises completed! {emoji}",
230-
emoji = Emoji("🎉", "★")
231-
);
232-
println!();
233-
println!("+----------------------------------------------------+");
234-
println!("| You made it to the Fe-nish line! |");
235-
println!("+-------------------------- ------------------------+");
236-
println!(" \\/ ");
237-
println!(" ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ");
238-
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
239-
println!(" ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ");
240-
println!(" ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ ");
241-
println!(" ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ ");
242-
println!(" ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ ");
243-
println!(" ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ ");
244-
println!(" ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ ");
245-
println!(" ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ");
246-
println!(" ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ ");
247-
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
248-
println!(" ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ");
249-
println!(" ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ ");
250-
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ");
251-
println!(" ▒▒ ▒▒ ▒▒ ▒▒ ");
252-
println!();
253-
println!("We hope you enjoyed learning about the various aspects of Rust!");
254-
println!(
255-
"If you noticed any issues, please don't hesitate to report them to our repo."
256-
);
257-
println!("You can also contribute your own exercises to help the greater community!");
258-
println!();
259-
println!("Before reporting an issue or contributing, please read our guidelines:");
260-
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
261270
}
262271
}
263272
}
264273

265-
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
274+
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_quit: Arc<AtomicBool>) {
266275
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
267276
println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
268277
thread::spawn(move || loop {
269278
let mut input = String::new();
270279
match io::stdin().read_line(&mut input) {
271280
Ok(_) => {
272281
let input = input.trim();
273-
if input.eq("hint") {
282+
if input == "hint" {
274283
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
275284
println!("{}", hint);
276285
}
277-
} else if input.eq("clear") {
286+
} else if input == "clear" {
278287
println!("\x1B[2J\x1B[1;1H");
279288
} else if input.eq("quit") {
289+
should_quit.store(true, Ordering::SeqCst);
280290
println!("Bye!");
281-
std::process::exit(0);
282291
} else if input.eq("help") {
283292
println!("Commands available to you in watch mode:");
284293
println!(" hint - prints the current exercise's hint");
@@ -318,14 +327,20 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
318327
}
319328
}
320329

321-
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
330+
enum WatchStatus {
331+
Finished,
332+
Unfinished,
333+
}
334+
335+
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
322336
/* Clears the terminal with an ANSI escape code.
323337
Works in UNIX and newer Windows terminals. */
324338
fn clear_screen() {
325339
println!("\x1Bc");
326340
}
327341

328342
let (tx, rx) = channel();
343+
let should_quit = Arc::new(AtomicBool::new(false));
329344

330345
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
331346
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
@@ -334,12 +349,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
334349

335350
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
336351
let failed_exercise_hint = match verify(exercises.iter(), verbose) {
337-
Ok(_) => return Ok(()),
352+
Ok(_) => return Ok(WatchStatus::Finished),
338353
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
339354
};
340-
spawn_watch_shell(&failed_exercise_hint);
355+
spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
341356
loop {
342-
match rx.recv() {
357+
match rx.recv_timeout(Duration::from_secs(1)) {
343358
Ok(event) => match event {
344359
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
345360
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
@@ -355,7 +370,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
355370
);
356371
clear_screen();
357372
match verify(pending_exercises, verbose) {
358-
Ok(_) => return Ok(()),
373+
Ok(_) => return Ok(WatchStatus::Finished),
359374
Err(exercise) => {
360375
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
361376
*failed_exercise_hint = Some(to_owned_hint(exercise));
@@ -365,8 +380,15 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
365380
}
366381
_ => {}
367382
},
383+
Err(RecvTimeoutError::Timeout) => {
384+
// the timeout expired, just check the `should_quit` variable below then loop again
385+
}
368386
Err(e) => println!("watch error: {:?}", e),
369387
}
388+
// Check if we need to exit
389+
if should_quit.load(Ordering::SeqCst) {
390+
return Ok(WatchStatus::Unfinished);
391+
}
370392
}
371393
}
372394

0 commit comments

Comments
 (0)