command_stream/command_packages/
watch.rs1use super::CommandEnvironment;
3use std::io;
4use std::path::Path;
5use std::sync::Arc;
6use std::time::Duration;
7use tokio::sync::{mpsc, watch, RwLock};
8use tokio::task::JoinHandle;
9
10pub struct PackageWatcher {
11 stop: watch::Sender<bool>,
12 task: Option<JoinHandle<()>>,
13 events: mpsc::Receiver<Result<String, String>>,
14}
15
16impl PackageWatcher {
17 pub async fn next_event(&mut self) -> Option<Result<String, String>> {
18 self.events.recv().await
19 }
20
21 pub async fn close(mut self) -> io::Result<()> {
22 let _ = self.stop.send(true);
23 self.task.take().unwrap().await.map_err(io::Error::other)
24 }
25}
26
27impl Drop for PackageWatcher {
28 fn drop(&mut self) {
29 let _ = self.stop.send(true);
30 }
31}
32
33pub async fn watch_package(
37 environment: Arc<RwLock<CommandEnvironment>>,
38 path: impl AsRef<Path>,
39 interval: Duration,
40) -> io::Result<PackageWatcher> {
41 if interval < Duration::from_millis(10) {
42 return Err(io::Error::new(
43 io::ErrorKind::InvalidInput,
44 "Watch interval must be at least 10ms",
45 ));
46 }
47 let path = tokio::fs::canonicalize(path).await?;
48 let mut previous = tokio::fs::read(&path).await?;
49 environment
50 .write()
51 .await
52 .install_file(&path)
53 .await
54 .map_err(io::Error::other)?;
55 let (stop, mut receiver) = watch::channel(false);
56 let (tx, events) = mpsc::channel(32);
57 let task = tokio::spawn(async move {
58 let mut tick = tokio::time::interval(interval);
59 tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
60 loop {
61 tokio::select! {
62 _ = receiver.changed() => break,
63 _ = tick.tick() => {
64 match tokio::fs::read(&path).await {
65 Ok(next) if next != previous => {
66 previous = next;
67 let result = environment.write().await.reload_file(&path).await;
68 let _ = tx.try_send(result);
69 }
70 Ok(_) => {},
71 Err(error) => { let _ = tx.try_send(Err(error.to_string())); },
72 }
73 }
74 }
75 }
76 });
77 Ok(PackageWatcher {
78 stop,
79 task: Some(task),
80 events,
81 })
82}