From d1a965f019d0e8f22d5a57f0a7abd8cd4a8d0d0c Mon Sep 17 00:00:00 2001
From: mo8it <mo8it@proton.me>
Date: Wed, 10 Apr 2024 02:12:50 +0200
Subject: [PATCH] Make the list mode part of the watch mode

---
 src/main.rs        | 19 +++++++++++--------
 src/watch.rs       | 27 +++++++++++++++++++++++----
 src/watch/state.rs |  5 +++--
 3 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 504c02dc..fc83e0fd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,9 +16,11 @@ mod watch;
 use self::{
     consts::WELCOME,
     exercise::{Exercise, InfoFile},
+    list::list,
     run::run,
     state_file::StateFile,
     verify::{verify, VerifyState},
+    watch::{watch, WatchExit},
 };
 
 /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
@@ -52,8 +54,6 @@ enum Subcommands {
         /// The name of the exercise
         name: String,
     },
-    /// List the exercises available in Rustlings
-    List,
 }
 
 fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> {
@@ -112,14 +112,17 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini
     let mut state_file = StateFile::read_or_default(exercises);
 
     match args.command {
-        None | Some(Subcommands::Watch) => {
-            watch::watch(&state_file, exercises)?;
-        }
+        None | Some(Subcommands::Watch) => loop {
+            match watch(&mut state_file, exercises)? {
+                WatchExit::Shutdown => break,
+                // It is much easier to exit the watch mode, launch the list mode and then restart
+                // the watch mode instead of trying to pause the watch threads and correct the
+                // watch state.
+                WatchExit::List => list(&mut state_file, exercises)?,
+            }
+        },
         // `Init` is handled above.
         Some(Subcommands::Init) => (),
-        Some(Subcommands::List) => {
-            list::list(&mut state_file, exercises)?;
-        }
         Some(Subcommands::Run { name }) => {
             let (_, exercise) = find_exercise(&name, exercises)?;
             run(exercise).unwrap_or_else(|_| exit(1));
diff --git a/src/watch.rs b/src/watch.rs
index 6324eb36..004a13f6 100644
--- a/src/watch.rs
+++ b/src/watch.rs
@@ -18,9 +18,19 @@ use crate::{exercise::Exercise, state_file::StateFile};
 
 use self::state::WatchState;
 
+/// Returned by the watch mode to indicate what to do afterwards.
+pub enum WatchExit {
+    /// Exit the program.
+    Shutdown,
+    /// Enter the list mode and restart the watch mode afterwards.
+    List,
+}
+
+#[derive(Copy, Clone)]
 enum InputEvent {
     Hint,
     Clear,
+    List,
     Quit,
     Unrecognized,
 }
@@ -86,20 +96,26 @@ fn input_handler(tx: Sender<WatchEvent>) {
         let event = match stdin_buf.trim() {
             "h" | "hint" => InputEvent::Hint,
             "c" | "clear" => InputEvent::Clear,
+            "l" | "list" => InputEvent::List,
             "q" | "quit" => InputEvent::Quit,
             _ => InputEvent::Unrecognized,
         };
 
-        stdin_buf.clear();
-
         if tx.send(WatchEvent::Input(event)).is_err() {
             // The receiver was dropped.
             return;
         }
+
+        match event {
+            InputEvent::List | InputEvent::Quit => return,
+            _ => (),
+        }
+
+        stdin_buf.clear();
     }
 }
 
-pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<()> {
+pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<WatchExit> {
     let (tx, rx) = channel();
     let mut debouncer = new_debouncer(
         Duration::from_secs(1),
@@ -125,6 +141,9 @@ pub fn watch(state_file: &StateFile, exercises: &'static [Exercise]) -> Result<(
             WatchEvent::Input(InputEvent::Hint) => {
                 watch_state.show_hint()?;
             }
+            WatchEvent::Input(InputEvent::List) => {
+                return Ok(WatchExit::List);
+            }
             WatchEvent::Input(InputEvent::Clear) | WatchEvent::TerminalResize => {
                 watch_state.render()?;
             }
@@ -147,5 +166,5 @@ We hope you're enjoying learning Rust!
 If you want to continue working on the exercises at a later point, you can simply run `rustlings` again.
 ")?;
 
-    Ok(())
+    Ok(WatchExit::Shutdown)
 }
diff --git a/src/watch/state.rs b/src/watch/state.rs
index 4db9440b..393ea02c 100644
--- a/src/watch/state.rs
+++ b/src/watch/state.rs
@@ -36,10 +36,11 @@ impl<'a> WatchState<'a> {
         let writer = io::stdout().lock();
 
         let prompt = format!(
-            "\n\n{}int/{}lear/{}uit? ",
+            "\n\n{}int/{}lear/{}ist/{}uit? ",
             "h".bold(),
             "c".bold(),
-            "q".bold()
+            "l".bold(),
+            "q".bold(),
         )
         .into_bytes();