glory_cli/service/
patch.rs

1use 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        // Check if it's possible to patch
71        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}