Compare commits

..

1 commit

Author SHA1 Message Date
Kacper Poneta
8b34729686
Merge 59e8f70e55 into a55e848359 2024-09-23 21:08:07 +02:00
7 changed files with 41 additions and 114 deletions

32
Cargo.lock generated
View file

@ -95,9 +95,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.18" version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -105,9 +105,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.18" version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -117,9 +117,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.18" version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -289,9 +289,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.159" version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]] [[package]]
name = "libredox" name = "libredox"
@ -434,9 +434,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.6" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
] ]
@ -536,9 +536,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.8" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -620,9 +620,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.22" version = "0.22.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -846,9 +846,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.20" version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View file

@ -20,7 +20,7 @@ rust-version = "1.80"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1.0.210", features = ["derive"] } serde = { version = "1.0.210", features = ["derive"] }
toml_edit = { version = "0.22.22", default-features = false, features = ["parse", "serde"] } toml_edit = { version = "0.22.21", default-features = false, features = ["parse", "serde"] }
[package] [package]
name = "rustlings" name = "rustlings"
@ -48,7 +48,7 @@ include = [
[dependencies] [dependencies]
ahash = { version = "0.8.11", default-features = false } ahash = { version = "0.8.11", default-features = false }
anyhow = "1.0.89" anyhow = "1.0.89"
clap = { version = "4.5.18", features = ["derive"] } clap = { version = "4.5.17", features = ["derive"] }
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] } crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] } notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] }
os_pipe = "1.2.1" os_pipe = "1.2.1"

View file

@ -381,7 +381,7 @@ impl AppState {
// Return the exercise index of the first pending exercise found. // Return the exercise index of the first pending exercise found.
fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result<Option<usize>> { fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result<Option<usize>> {
stdout.write_all(FINAL_CHECK_MSG)?; stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?;
let n_exercises = self.exercises.len(); let n_exercises = self.exercises.len();
let status = thread::scope(|s| { let status = thread::scope(|s| {
@ -441,10 +441,7 @@ impl AppState {
/// Mark the current exercise as done and move on to the next pending exercise if one exists. /// Mark the current exercise as done and move on to the next pending exercise if one exists.
/// If all exercises are marked as done, run all of them to make sure that they are actually /// If all exercises are marked as done, run all of them to make sure that they are actually
/// done. If an exercise which is marked as done fails, mark it as pending and continue on it. /// done. If an exercise which is marked as done fails, mark it as pending and continue on it.
pub fn done_current_exercise<const CLEAR_BEFORE_FINAL_CHECK: bool>( pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
&mut self,
stdout: &mut StdoutLock,
) -> Result<ExercisesProgress> {
let exercise = &mut self.exercises[self.current_exercise_ind]; let exercise = &mut self.exercises[self.current_exercise_ind];
if !exercise.done { if !exercise.done {
exercise.done = true; exercise.done = true;
@ -456,12 +453,6 @@ impl AppState {
return Ok(ExercisesProgress::NewPending); return Ok(ExercisesProgress::NewPending);
} }
if CLEAR_BEFORE_FINAL_CHECK {
clear_terminal(stdout)?;
} else {
stdout.write_all(b"\n")?;
}
if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? { if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? {
stdout.write_all(b"\n\n")?; stdout.write_all(b"\n\n")?;
@ -491,7 +482,8 @@ impl AppState {
const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises";
const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n"; const STATE_FILE_HEADER: &[u8] = b"DON'T EDIT THIS FILE!\n\n";
const FINAL_CHECK_MSG: &[u8] = b"All exercises seem to be done. const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b"
All exercises seem to be done.
Recompiling and running all exercises to make sure that all of them are actually done. Recompiling and running all exercises to make sure that all of them are actually done.
"; ";
const FENISH_LINE: &str = "+----------------------------------------------------+ const FENISH_LINE: &str = "+----------------------------------------------------+

View file

@ -44,7 +44,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> {
stdout.write_all(b"\n")?; stdout.write_all(b"\n")?;
} }
match app_state.done_current_exercise::<false>(&mut stdout)? { match app_state.done_current_exercise(&mut stdout)? {
ExercisesProgress::NewPending | ExercisesProgress::CurrentPending => { ExercisesProgress::NewPending | ExercisesProgress::CurrentPending => {
stdout.write_all(b"Next exercise: ")?; stdout.write_all(b"Next exercise: ")?;
app_state app_state

View file

@ -69,11 +69,11 @@ fn run_watch(
// Prevent dropping the guard until the end of the function. // Prevent dropping the guard until the end of the function.
// Otherwise, the file watcher exits. // Otherwise, the file watcher exits.
let _watcher_guard = if let Some(exercise_names) = notify_exercise_names { let _watcher_guard = if let Some(exercise_names) = notify_exercise_names {
let notify_event_handler =
NotifyEventHandler::build(watch_event_sender.clone(), exercise_names)?;
let mut watcher = RecommendedWatcher::new( let mut watcher = RecommendedWatcher::new(
notify_event_handler, NotifyEventHandler {
sender: watch_event_sender.clone(),
exercise_names,
},
Config::default().with_poll_interval(Duration::from_secs(1)), Config::default().with_poll_interval(Duration::from_secs(1)),
) )
.inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?; .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?;

View file

@ -1,71 +1,15 @@
use anyhow::{Context, Result};
use notify::{ use notify::{
event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode}, event::{MetadataKind, ModifyKind},
Event, EventKind, Event, EventKind,
}; };
use std::{ use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender};
sync::{
atomic::Ordering::Relaxed,
mpsc::{sync_channel, RecvTimeoutError, Sender, SyncSender},
},
thread,
time::Duration,
};
use super::{WatchEvent, EXERCISE_RUNNING}; use super::{WatchEvent, EXERCISE_RUNNING};
const DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
pub struct NotifyEventHandler { pub struct NotifyEventHandler {
error_sender: Sender<WatchEvent>, pub sender: Sender<WatchEvent>,
// Sends the index of the updated exercise. /// Used to report which exercise was modified.
update_sender: SyncSender<usize>, pub exercise_names: &'static [&'static [u8]],
// Used to report which exercise was modified.
exercise_names: &'static [&'static [u8]],
}
impl NotifyEventHandler {
pub fn build(
watch_event_sender: Sender<WatchEvent>,
exercise_names: &'static [&'static [u8]],
) -> Result<Self> {
let (update_sender, update_receiver) = sync_channel(0);
let error_sender = watch_event_sender.clone();
// Debouncer
thread::Builder::new()
.spawn(move || {
let mut exercise_updated = vec![false; exercise_names.len()];
loop {
match update_receiver.recv_timeout(DEBOUNCE_DURATION) {
Ok(exercise_ind) => exercise_updated[exercise_ind] = true,
Err(RecvTimeoutError::Timeout) => {
for (exercise_ind, updated) in exercise_updated.iter_mut().enumerate() {
if *updated {
if watch_event_sender
.send(WatchEvent::FileChange { exercise_ind })
.is_err()
{
break;
}
*updated = false;
}
}
}
Err(RecvTimeoutError::Disconnected) => break,
}
}
})
.context("Failed to spawn a thread to debounce file changes")?;
Ok(Self {
error_sender,
update_sender,
exercise_names,
})
}
} }
impl notify::EventHandler for NotifyEventHandler { impl notify::EventHandler for NotifyEventHandler {
@ -78,8 +22,8 @@ impl notify::EventHandler for NotifyEventHandler {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
// An error occurs when the receiver is dropped. // An error occurs when the receiver is dropped.
// After dropping the receiver, the watcher guard should also be dropped. // After dropping the receiver, the debouncer guard should also be dropped.
let _ = self.error_sender.send(WatchEvent::NotifyErr(e)); let _ = self.sender.send(WatchEvent::NotifyErr(e));
return; return;
} }
}; };
@ -88,10 +32,6 @@ impl notify::EventHandler for NotifyEventHandler {
EventKind::Any => (), EventKind::Any => (),
EventKind::Modify(modify_kind) => match modify_kind { EventKind::Modify(modify_kind) => match modify_kind {
ModifyKind::Any | ModifyKind::Data(_) => (), ModifyKind::Any | ModifyKind::Data(_) => (),
ModifyKind::Name(rename_mode) => match rename_mode {
RenameMode::Any | RenameMode::To => (),
RenameMode::From | RenameMode::Both | RenameMode::Other => return,
},
ModifyKind::Metadata(metadata_kind) => match metadata_kind { ModifyKind::Metadata(metadata_kind) => match metadata_kind {
MetadataKind::Any | MetadataKind::WriteTime => (), MetadataKind::Any | MetadataKind::WriteTime => (),
MetadataKind::AccessTime MetadataKind::AccessTime
@ -100,17 +40,12 @@ impl notify::EventHandler for NotifyEventHandler {
| MetadataKind::Extended | MetadataKind::Extended
| MetadataKind::Other => return, | MetadataKind::Other => return,
}, },
ModifyKind::Other => return, ModifyKind::Name(_) | ModifyKind::Other => return,
}, },
EventKind::Access(access_kind) => match access_kind { EventKind::Access(_)
AccessKind::Any => (), | EventKind::Create(_)
AccessKind::Close(access_mode) => match access_mode { | EventKind::Remove(_)
AccessMode::Any | AccessMode::Write => (), | EventKind::Other => return,
AccessMode::Execute | AccessMode::Read | AccessMode::Other => return,
},
AccessKind::Read | AccessKind::Open(_) | AccessKind::Other => return,
},
EventKind::Create(_) | EventKind::Remove(_) | EventKind::Other => return,
} }
let _ = input_event let _ = input_event
@ -127,6 +62,6 @@ impl notify::EventHandler for NotifyEventHandler {
.iter() .iter()
.position(|exercise_name| *exercise_name == file_name_without_ext) .position(|exercise_name| *exercise_name == file_name_without_ext)
}) })
.try_for_each(|exercise_ind| self.update_sender.send(exercise_ind)); .try_for_each(|exercise_ind| self.sender.send(WatchEvent::FileChange { exercise_ind }));
} }
} }

View file

@ -113,7 +113,7 @@ impl<'a> WatchState<'a> {
return Ok(ExercisesProgress::CurrentPending); return Ok(ExercisesProgress::CurrentPending);
} }
self.app_state.done_current_exercise::<true>(stdout) self.app_state.done_current_exercise(stdout)
} }
fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> { fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> {