Merge pull request #843 from abusch/quit_command
feat: Add "quit" command to `rustlings watch`
This commit is contained in:
commit
4a18bdeefe
44
src/main.rs
44
src/main.rs
@ -10,7 +10,8 @@ use std::fs;
|
||||
use std::io::{self, prelude::*};
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{RecvTimeoutError, channel};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@ -217,7 +218,8 @@ fn main() {
|
||||
}
|
||||
|
||||
Subcommands::Watch(_subargs) => {
|
||||
if let Err(e) = watch(&exercises, verbose) {
|
||||
match watch(&exercises, verbose) {
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Error: Could not watch your progress. Error message was {:?}.",
|
||||
e
|
||||
@ -225,6 +227,7 @@ fn main() {
|
||||
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("🎉", "★")
|
||||
@ -259,10 +262,16 @@ fn main() {
|
||||
println!("Before reporting an issue or contributing, please read our guidelines:");
|
||||
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
|
||||
}
|
||||
Ok(WatchStatus::Unfinished) => {
|
||||
println!("We hope you're enjoying learning about Rust!");
|
||||
println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
|
||||
fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>, should_quit: Arc<AtomicBool>) {
|
||||
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 {
|
||||
@ -270,15 +279,15 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
|
||||
match io::stdin().read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let input = input.trim();
|
||||
if input.eq("hint") {
|
||||
if input == "hint" {
|
||||
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
|
||||
println!("{}", hint);
|
||||
}
|
||||
} else if input.eq("clear") {
|
||||
} else if input == "clear" {
|
||||
println!("\x1B[2J\x1B[1;1H");
|
||||
} else if input.eq("quit") {
|
||||
should_quit.store(true, Ordering::SeqCst);
|
||||
println!("Bye!");
|
||||
std::process::exit(0);
|
||||
} else if input.eq("help") {
|
||||
println!("Commands available to you in watch mode:");
|
||||
println!(" hint - prints the current exercise's hint");
|
||||
@ -318,7 +327,12 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
|
||||
}
|
||||
}
|
||||
|
||||
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||
enum WatchStatus {
|
||||
Finished,
|
||||
Unfinished,
|
||||
}
|
||||
|
||||
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
|
||||
/* Clears the terminal with an ANSI escape code.
|
||||
Works in UNIX and newer Windows terminals. */
|
||||
fn clear_screen() {
|
||||
@ -326,6 +340,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let should_quit = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
||||
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
||||
@ -334,12 +349,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||
|
||||
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
||||
let failed_exercise_hint = match verify(exercises.iter(), verbose) {
|
||||
Ok(_) => return Ok(()),
|
||||
Ok(_) => return Ok(WatchStatus::Finished),
|
||||
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
|
||||
};
|
||||
spawn_watch_shell(&failed_exercise_hint);
|
||||
spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit));
|
||||
loop {
|
||||
match rx.recv() {
|
||||
match rx.recv_timeout(Duration::from_secs(1)) {
|
||||
Ok(event) => match event {
|
||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
||||
@ -355,7 +370,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||
);
|
||||
clear_screen();
|
||||
match verify(pending_exercises, verbose) {
|
||||
Ok(_) => return Ok(()),
|
||||
Ok(_) => return Ok(WatchStatus::Finished),
|
||||
Err(exercise) => {
|
||||
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
||||
*failed_exercise_hint = Some(to_owned_hint(exercise));
|
||||
@ -365,8 +380,15 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Err(RecvTimeoutError::Timeout) => {
|
||||
// the timeout expired, just check the `should_quit` variable below then loop again
|
||||
}
|
||||
Err(e) => println!("watch error: {:?}", e),
|
||||
}
|
||||
// Check if we need to exit
|
||||
if should_quit.load(Ordering::SeqCst) {
|
||||
return Ok(WatchStatus::Unfinished);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user