2021-08-19 11:28:56 +02:00
|
|
|
use std::{mem::take, sync::Arc};
|
2021-08-18 15:12:50 +02:00
|
|
|
|
2021-08-19 16:55:34 +02:00
|
|
|
use atomic_take::AtomicTake;
|
2021-08-18 15:12:50 +02:00
|
|
|
use futures::FutureExt;
|
|
|
|
use tokio::{
|
|
|
|
spawn,
|
|
|
|
sync::{mpsc, watch, Notify},
|
|
|
|
task::{JoinError, JoinHandle},
|
|
|
|
try_join,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
config::Config,
|
|
|
|
error::{CriticalError, ReconfigError},
|
|
|
|
fs, signal,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Watchexec {
|
2021-08-19 16:55:34 +02:00
|
|
|
handle: Arc<AtomicTake<JoinHandle<Result<(), CriticalError>>>>,
|
2021-08-18 15:12:50 +02:00
|
|
|
start_lock: Arc<Notify>,
|
|
|
|
fs_watch: watch::Sender<fs::WorkingData>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Watchexec {
|
2021-08-19 16:55:34 +02:00
|
|
|
/// TODO
|
|
|
|
///
|
|
|
|
/// Returns an [`Arc`] for convenience; use [`try_unwrap`][Arc::try_unwrap()] to get the value
|
|
|
|
/// directly if needed.
|
|
|
|
pub fn new(mut config: Config) -> Result<Arc<Self>, CriticalError> {
|
2021-08-19 11:28:56 +02:00
|
|
|
let (fs_s, fs_r) = watch::channel(take(&mut config.fs));
|
2021-08-18 15:12:50 +02:00
|
|
|
|
|
|
|
let notify = Arc::new(Notify::new());
|
|
|
|
let start_lock = notify.clone();
|
|
|
|
let handle = spawn(async move {
|
|
|
|
notify.notified().await;
|
|
|
|
|
2021-08-19 11:28:56 +02:00
|
|
|
let (er_s, er_r) = mpsc::channel(config.error_channel_size);
|
|
|
|
let (ev_s, ev_r) = mpsc::channel(config.event_channel_size);
|
2021-08-18 15:12:50 +02:00
|
|
|
|
|
|
|
macro_rules! subtask {
|
|
|
|
($task:expr) => {
|
|
|
|
spawn($task).then(|jr| async { flatten(jr) })
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let fs = subtask!(fs::worker(fs_r, er_s.clone(), ev_s.clone()));
|
|
|
|
let signal = subtask!(signal::worker(er_s.clone(), ev_s.clone()));
|
|
|
|
|
|
|
|
try_join!(fs, signal).map(drop)
|
|
|
|
});
|
|
|
|
|
2021-08-19 16:55:34 +02:00
|
|
|
Ok(Arc::new(Self {
|
|
|
|
handle: Arc::new(AtomicTake::new(handle)),
|
2021-08-18 15:12:50 +02:00
|
|
|
start_lock,
|
|
|
|
fs_watch: fs_s,
|
2021-08-19 16:55:34 +02:00
|
|
|
}))
|
2021-08-18 15:12:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reconfig(&self, config: Config) -> Result<(), ReconfigError> {
|
|
|
|
self.fs_watch.send(config.fs)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-08-19 16:55:34 +02:00
|
|
|
/// Start watchexec and obtain the handle to its main task.
|
|
|
|
///
|
|
|
|
/// This must only be called once.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
/// Panics if called twice.
|
|
|
|
pub fn main(&self) -> JoinHandle<Result<(), CriticalError>> {
|
2021-08-18 15:12:50 +02:00
|
|
|
self.start_lock.notify_one();
|
2021-08-19 16:55:34 +02:00
|
|
|
self.handle
|
|
|
|
.take()
|
|
|
|
.expect("Watchexec::main was called twice")
|
2021-08-18 15:12:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn flatten(join_res: Result<Result<(), CriticalError>, JoinError>) -> Result<(), CriticalError> {
|
|
|
|
join_res
|
|
|
|
.map_err(CriticalError::MainTaskJoin)
|
|
|
|
.and_then(|x| x)
|
|
|
|
}
|