use anyhow::{Error, Result}; use notify_debouncer_mini::{ new_debouncer, notify::{self, RecursiveMode}, }; use std::{ io::{self, Write}, path::Path, sync::mpsc::channel, thread, time::Duration, }; mod debounce_event; mod state; mod terminal_event; use crate::{exercise::Exercise, state_file::StateFile}; use self::{ debounce_event::DebounceEventHandler, state::WatchState, terminal_event::{terminal_event_handler, InputEvent}, }; enum WatchEvent { Input(InputEvent), FileChange { exercise_ind: usize }, NotifyErr(notify::Error), TerminalEventErr(io::Error), TerminalResize, } /// 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, } pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), exercises, }, )?; debouncer .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; let mut watch_state = WatchState::new(state_file, exercises); // TODO: bool watch_state.run_exercise()?; watch_state.render()?; thread::spawn(move || terminal_event_handler(tx)); while let Ok(event) = rx.recv() { match event { WatchEvent::Input(InputEvent::Hint) => { watch_state.show_hint()?; } WatchEvent::Input(InputEvent::List) => { return Ok(WatchExit::List); } WatchEvent::TerminalResize => { watch_state.render()?; } WatchEvent::Input(InputEvent::Quit) => break, WatchEvent::Input(InputEvent::Unrecognized(cmd)) => { watch_state.handle_invalid_cmd(&cmd)?; } WatchEvent::FileChange { exercise_ind } => { // TODO: bool watch_state.run_exercise_with_ind(exercise_ind)?; watch_state.render()?; } WatchEvent::NotifyErr(e) => { return Err(Error::from(e).context("Exercise file watcher failed")) } WatchEvent::TerminalEventErr(e) => { return Err(Error::from(e).context("Terminal event listener failed")) } } } watch_state.into_writer().write_all(b" 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(WatchExit::Shutdown) }