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::io::{self, prelude::*};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{Command, Stdio};
|
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::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -217,7 +218,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Subcommands::Watch(_subargs) => {
|
Subcommands::Watch(_subargs) => {
|
||||||
if let Err(e) = watch(&exercises, verbose) {
|
match watch(&exercises, verbose) {
|
||||||
|
Err(e) => {
|
||||||
println!(
|
println!(
|
||||||
"Error: Could not watch your progress. Error message was {:?}.",
|
"Error: Could not watch your progress. Error message was {:?}.",
|
||||||
e
|
e
|
||||||
|
@ -225,6 +227,7 @@ fn main() {
|
||||||
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) => {
|
||||||
println!(
|
println!(
|
||||||
"{emoji} All exercises completed! {emoji}",
|
"{emoji} All exercises completed! {emoji}",
|
||||||
emoji = Emoji("🎉", "★")
|
emoji = Emoji("🎉", "★")
|
||||||
|
@ -259,10 +262,16 @@ fn main() {
|
||||||
println!("Before reporting an issue or contributing, please read our guidelines:");
|
println!("Before reporting an issue or contributing, please read our guidelines:");
|
||||||
println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md");
|
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);
|
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 {
|
||||||
|
@ -270,15 +279,15 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc<Mutex<Option<String>>>) {
|
||||||
match io::stdin().read_line(&mut input) {
|
match io::stdin().read_line(&mut input) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let input = input.trim();
|
let input = input.trim();
|
||||||
if input.eq("hint") {
|
if input == "hint" {
|
||||||
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
|
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
|
||||||
println!("{}", hint);
|
println!("{}", hint);
|
||||||
}
|
}
|
||||||
} else if input.eq("clear") {
|
} else if input == "clear" {
|
||||||
println!("\x1B[2J\x1B[1;1H");
|
println!("\x1B[2J\x1B[1;1H");
|
||||||
} else if input.eq("quit") {
|
} else if input.eq("quit") {
|
||||||
|
should_quit.store(true, Ordering::SeqCst);
|
||||||
println!("Bye!");
|
println!("Bye!");
|
||||||
std::process::exit(0);
|
|
||||||
} else if input.eq("help") {
|
} else if input.eq("help") {
|
||||||
println!("Commands available to you in watch mode:");
|
println!("Commands available to you in watch mode:");
|
||||||
println!(" hint - prints the current exercise's hint");
|
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.
|
/* Clears the terminal with an ANSI escape code.
|
||||||
Works in UNIX and newer Windows terminals. */
|
Works in UNIX and newer Windows terminals. */
|
||||||
fn clear_screen() {
|
fn clear_screen() {
|
||||||
|
@ -326,6 +340,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
let should_quit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
||||||
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
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 to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
||||||
let failed_exercise_hint = match verify(exercises.iter(), verbose) {
|
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)))),
|
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 {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv_timeout(Duration::from_secs(1)) {
|
||||||
Ok(event) => match event {
|
Ok(event) => match event {
|
||||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||||
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
||||||
|
@ -355,7 +370,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> {
|
||||||
);
|
);
|
||||||
clear_screen();
|
clear_screen();
|
||||||
match verify(pending_exercises, verbose) {
|
match verify(pending_exercises, verbose) {
|
||||||
Ok(_) => return Ok(()),
|
Ok(_) => return Ok(WatchStatus::Finished),
|
||||||
Err(exercise) => {
|
Err(exercise) => {
|
||||||
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
||||||
*failed_exercise_hint = Some(to_owned_hint(exercise));
|
*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),
|
Err(e) => println!("watch error: {:?}", e),
|
||||||
}
|
}
|
||||||
|
// Check if we need to exit
|
||||||
|
if should_quit.load(Ordering::SeqCst) {
|
||||||
|
return Ok(WatchStatus::Unfinished);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue