glory_cli/service/
patch.rs1use crate::config::Project;
2use crate::ext::anyhow::Result;
3use crate::signal::{Interrupt, ReloadSignal};
4use crate::{
5 ext::{remove_nested, PathBufExt},
6 logger::GRAY,
7};
8use camino::Utf8PathBuf;
9use glory_hot_reload::ViewMacros;
10use itertools::Itertools;
11use notify::{DebouncedEvent, RecursiveMode, Watcher};
12use std::collections::HashSet;
13use std::sync::Arc;
14use std::time::Duration;
15use tokio::task::JoinHandle;
16
17use super::notify::Watched;
18
19pub async fn spawn(proj: &Arc<Project>, view_macros: &ViewMacros) -> Result<JoinHandle<()>> {
20 let view_macros = view_macros.to_owned();
21 let mut set: HashSet<Utf8PathBuf> = HashSet::from_iter(vec![]);
22
23 set.extend(proj.lib.src_paths.clone());
24
25 let paths = remove_nested(set.into_iter());
26
27 log::info!("Patch watching folders {}", GRAY.paint(paths.iter().join(", ")));
28 let proj = proj.clone();
29
30 Ok(tokio::spawn(async move { run(&paths, proj, view_macros).await }))
31}
32
33async fn run(paths: &[Utf8PathBuf], proj: Arc<Project>, view_macros: ViewMacros) {
34 let (sync_tx, sync_rx) = std::sync::mpsc::channel::<DebouncedEvent>();
35
36 let proj = proj.clone();
37 std::thread::spawn(move || {
38 while let Ok(event) = sync_rx.recv() {
39 match Watched::try_new(&event, &proj) {
40 Ok(Some(watched)) => handle(watched, proj.clone(), view_macros.clone()),
41 Err(e) => log::error!("Notify error {e}"),
42 _ => log::trace!("Notify not handled {}", GRAY.paint(format!("{:?}", event))),
43 }
44 }
45 log::debug!("Notify stopped");
46 });
47
48 let mut watcher = notify::watcher(sync_tx, Duration::from_millis(200)).expect("failed to build file system watcher");
49
50 for path in paths {
51 if let Err(e) = watcher.watch(path, RecursiveMode::Recursive) {
52 log::error!("Notify could not watch {path:?} due to {e:?}");
53 }
54 }
55
56 if let Err(e) = Interrupt::subscribe_shutdown().recv().await {
57 log::trace!("Notify stopped due to: {e:?}");
58 }
59}
60
61fn handle(watched: Watched, proj: Arc<Project>, view_macros: ViewMacros) {
62 log::trace!("Notify handle {}", GRAY.paint(format!("{:?}", watched.path())));
63
64 let Some(path) = watched.path() else {
65 Interrupt::send_all_changed();
66 return;
67 };
68
69 if path.starts_with_any(&proj.lib.src_paths) && path.is_ext_any(&["rs"]) {
70 let patches = view_macros.patch(path);
72 if let Ok(Some(patch)) = patches {
73 log::debug!("Patching view.");
74 ReloadSignal::send_view_patches(&patch);
75 }
76 }
77}