diff options
| author | mo8it <mo8it@proton.me> | 2024-04-07 19:29:16 +0200 |
|---|---|---|
| committer | mo8it <mo8it@proton.me> | 2024-04-07 19:29:16 +0200 |
| commit | 9a4ee47c527251fc3efacacc31bd0e73ef527969 (patch) | |
| tree | 5d52e641af2c4a18b1bbcabed4536f5c6a2fa3dd /src/watch.rs | |
| parent | 0a674a158da0d519f03a88bfabf31d98c0e064c6 (diff) | |
Separate WatchState
Diffstat (limited to 'src/watch.rs')
| -rw-r--r-- | src/watch.rs | 181 |
1 files changed, 12 insertions, 169 deletions
diff --git a/src/watch.rs b/src/watch.rs index 1503fdf..967f98c 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -1,25 +1,18 @@ use anyhow::Result; -use crossterm::{ - style::{Attribute, ContentStyle, Stylize}, - terminal::{Clear, ClearType}, - ExecutableCommand, -}; -use notify_debouncer_mini::{ - new_debouncer, notify::RecursiveMode, DebounceEventResult, DebouncedEventKind, -}; +use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode}; use std::{ - fmt::Write as _, - io::{self, BufRead, StdoutLock, Write}, + io::{self, BufRead, Write}, path::Path, - sync::mpsc::{channel, sync_channel, Receiver}, + sync::mpsc::{channel, sync_channel}, thread, time::Duration, }; -use crate::{ - exercise::{self, Exercise}, - state_file::StateFile, -}; +mod state; + +use crate::{exercise::Exercise, state_file::StateFile}; + +use self::state::WatchState; enum Event { Hint, @@ -27,130 +20,6 @@ enum Event { Quit, } -struct WatchState<'a> { - writer: StdoutLock<'a>, - rx: Receiver<DebounceEventResult>, - exercises: &'a [Exercise], - exercise: &'a Exercise, - current_exercise_ind: usize, - stdout: Option<Vec<u8>>, - stderr: Option<Vec<u8>>, - message: Option<String>, - prompt: Vec<u8>, -} - -impl<'a> WatchState<'a> { - fn run_exercise(&mut self) -> Result<bool> { - let output = self.exercise.run()?; - - if !output.status.success() { - self.stdout = Some(output.stdout); - self.stderr = Some(output.stderr); - return Ok(false); - } - - if let exercise::State::Pending(context) = self.exercise.state()? { - let mut message = format!( - " -You can keep working on this exercise or jump into the next one by removing the {} comment: - -", - "`I AM NOT DONE`".bold(), - ); - - for context_line in context { - let formatted_line = if context_line.important { - context_line.line.bold() - } else { - context_line.line.stylize() - }; - - writeln!( - message, - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - )?; - } - - self.stdout = Some(output.stdout); - self.message = Some(message); - return Ok(false); - } - - Ok(true) - } - - fn try_recv_event(&mut self) -> Result<()> { - let Ok(events) = self.rx.recv_timeout(Duration::from_millis(100)) else { - return Ok(()); - }; - - if let Some(current_exercise_ind) = events? - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - { - self.current_exercise_ind = current_exercise_ind; - } else { - return Ok(()); - }; - - while self.current_exercise_ind < self.exercises.len() { - self.exercise = &self.exercises[self.current_exercise_ind]; - if !self.run_exercise()? { - break; - } - - self.current_exercise_ind += 1; - } - - Ok(()) - } - - fn prompt(&mut self) -> io::Result<()> { - self.writer.write_all(&self.prompt)?; - self.writer.flush() - } - - fn render(&mut self) -> Result<()> { - self.writer.execute(Clear(ClearType::All))?; - - if let Some(stdout) = &self.stdout { - self.writer.write_all(stdout)?; - } - - if let Some(stderr) = &self.stderr { - self.writer.write_all(stderr)?; - } - - if let Some(message) = &self.message { - self.writer.write_all(message.as_bytes())?; - } - - self.prompt()?; - - Ok(()) - } -} - pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { let (tx, rx) = channel(); let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; @@ -158,29 +27,7 @@ pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let current_exercise_ind = state_file.next_exercise_ind(); - - let exercise = &exercises[current_exercise_ind]; - - let writer = io::stdout().lock(); - - let mut watch_state = WatchState { - writer, - rx, - exercises, - exercise, - current_exercise_ind, - stdout: None, - stderr: None, - message: None, - prompt: format!( - "\n\n{}int/{}lear/{}uit? ", - "h".bold(), - "c".bold(), - "q".bold() - ) - .into_bytes(), - }; + let mut watch_state = WatchState::new(state_file, exercises, rx); watch_state.run_exercise()?; watch_state.render()?; @@ -214,24 +61,20 @@ pub fn watch(state_file: &StateFile, exercises: &[Exercise]) -> Result<()> { if let Ok(event) = rx.try_recv() { match event { Some(Event::Hint) => { - watch_state - .writer - .write_all(watch_state.exercise.hint.as_bytes())?; - watch_state.prompt()?; + watch_state.show_hint()?; } Some(Event::Clear) => { watch_state.render()?; } Some(Event::Quit) => break, None => { - watch_state.writer.write_all(b"Invalid command")?; - watch_state.prompt()?; + watch_state.handle_invalid_cmd()?; } } } } - watch_state.writer.write_all(b" + 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. ")?; |
