summaryrefslogtreecommitdiffstats
path: root/src/watcher.rs
blob: 42fe8dcfaea2d5ad31980d774eebd186c5e6ace0 (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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use anyhow::Result;
use crossbeam_channel::{unbounded, Sender};
use notify::{Error, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_mini::{new_debouncer, DebouncedEvent};
use scopetime::scope_time;
use std::{path::Path, thread, time::Duration};

pub struct RepoWatcher {
	receiver: crossbeam_channel::Receiver<()>,
}

impl RepoWatcher {
	pub fn new(workdir: &str) -> Self {
		log::trace!(
			"recommended watcher: {:?}",
			RecommendedWatcher::kind()
		);

		let (tx, rx) = std::sync::mpsc::channel();

		let workdir = workdir.to_string();

		thread::spawn(move || {
			let timeout = Duration::from_secs(2);
			create_watcher(timeout, tx, &workdir);
		});

		let (out_tx, out_rx) = unbounded();

		thread::spawn(move || {
			if let Err(e) = Self::forwarder(&rx, &out_tx) {
				//maybe we need to restart the forwarder now?
				log::error!("notify receive error: {}", e);
			}
		});

		Self { receiver: out_rx }
	}

	///
	pub fn receiver(&self) -> crossbeam_channel::Receiver<()> {
		self.receiver.clone()
	}

	fn forwarder(
		receiver: &std::sync::mpsc::Receiver<
			Result<Vec<DebouncedEvent>, Vec<Error>>,
		>,
		sender: &Sender<()>,
	) -> Result<()> {
		loop {
			let ev = receiver.recv()?;

			if let Ok(ev) = ev {
				log::debug!("notify events: {}", ev.len());

				for (idx, ev) in ev.iter().enumerate() {
					log::debug!("notify [{}]: {:?}", idx, ev);
				}

				if !ev.is_empty() {
					sender.send(())?;
				}
			}
		}
	}
}

fn create_watcher(
	timeout: Duration,
	tx: std::sync::mpsc::Sender<
		Result<Vec<DebouncedEvent>, Vec<Error>>,
	>,
	workdir: &str,
) {
	scope_time!("create_watcher");

	let mut bouncer =
		new_debouncer(timeout, None, tx).expect("Watch create error");
	bouncer
		.watcher()
		.watch(Path::new(&workdir), RecursiveMode::Recursive)
		.expect("Watch error");

	std::mem::forget(bouncer);
}