summaryrefslogtreecommitdiff
path: root/src/watch/notify_event.rs
blob: 5ed8fd183f350079201ded5020d31457b33a6f2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use notify::{
    event::{MetadataKind, ModifyKind},
    Event, EventKind,
};
use std::sync::{atomic::Ordering::Relaxed, mpsc::Sender};

use super::{WatchEvent, EXERCISE_RUNNING};

pub struct NotifyEventHandler {
    pub sender: Sender<WatchEvent>,
    /// Used to report which exercise was modified.
    pub exercise_names: &'static [&'static [u8]],
}

impl notify::EventHandler for NotifyEventHandler {
    fn handle_event(&mut self, input_event: notify::Result<Event>) {
        if EXERCISE_RUNNING.load(Relaxed) {
            return;
        }

        let input_event = match input_event {
            Ok(v) => v,
            Err(e) => {
                // An error occurs when the receiver is dropped.
                // After dropping the receiver, the debouncer guard should also be dropped.
                let _ = self.sender.send(WatchEvent::NotifyErr(e));
                return;
            }
        };

        match input_event.kind {
            EventKind::Any => (),
            EventKind::Modify(modify_kind) => match modify_kind {
                ModifyKind::Any | ModifyKind::Data(_) => (),
                ModifyKind::Metadata(metadata_kind) => match metadata_kind {
                    MetadataKind::Any | MetadataKind::WriteTime => (),
                    MetadataKind::AccessTime
                    | MetadataKind::Permissions
                    | MetadataKind::Ownership
                    | MetadataKind::Extended
                    | MetadataKind::Other => return,
                },
                ModifyKind::Name(_) | ModifyKind::Other => return,
            },
            EventKind::Access(_)
            | EventKind::Create(_)
            | EventKind::Remove(_)
            | EventKind::Other => return,
        }

        let _ = input_event
            .paths
            .into_iter()
            .filter_map(|path| {
                let file_name = path.file_name()?.to_str()?.as_bytes();

                let [file_name_without_ext @ .., b'.', b'r', b's'] = file_name else {
                    return None;
                };

                self.exercise_names
                    .iter()
                    .position(|exercise_name| *exercise_name == file_name_without_ext)
            })
            .try_for_each(|exercise_ind| self.sender.send(WatchEvent::FileChange { exercise_ind }));
    }
}