Skip to main content

hyperlane_cli/watch/
fn.rs

1use super::*;
2
3/// Execute cargo run command and capture output through log.
4///
5/// # Returns
6///
7/// - `Result<(), io::Error>` - Success or error
8async fn run_cargo_run() -> Result<(), io::Error> {
9    let output: Output = Command::new(CARGO_RUN_PROGRAM)
10        .arg("run")
11        .stdout(Stdio::piped())
12        .stderr(Stdio::piped())
13        .output()
14        .await?;
15    let stdout: String = String::from_utf8_lossy(&output.stdout).trim().to_string();
16    let stderr: String = String::from_utf8_lossy(&output.stderr).trim().to_string();
17    if !stdout.is_empty() {
18        for line in stdout.lines() {
19            log::info!("{line}");
20        }
21    }
22    if !stderr.is_empty() {
23        if output.status.success() {
24            for line in stderr.lines() {
25                if line.is_empty() {
26                    continue;
27                }
28                log::info!("{line}");
29            }
30        } else {
31            for line in stderr.lines() {
32                if line.is_empty() {
33                    continue;
34                }
35                log::error!("{line}");
36            }
37            log::error!("cargo run failed");
38        }
39    }
40    Ok(())
41}
42
43/// Execute watch command using notify crate to monitor file changes
44/// and re-run cargo run when source files are modified.
45///
46/// # Returns
47///
48/// - `Result<(), io::Error>` - Success or error
49pub async fn execute_watch() -> Result<(), io::Error> {
50    let src_path: PathBuf = PathBuf::from("src");
51    if !src_path.exists() {
52        return Err(io::Error::other(ERROR_SRC_DIRECTORY_NOT_FOUND));
53    }
54    run_cargo_run().await?;
55    let (tx, mut rx): (Sender<Event>, Receiver<Event>) = channel(Event::new(EventKind::Any));
56    let mut watcher: RecommendedWatcher =
57        recommended_watcher(move |result: Result<Event, notify::Error>| {
58            if let Ok(event) = result {
59                let _: Result<(), tokio::sync::watch::error::SendError<Event>> = tx.send(event);
60            }
61        })
62        .map_err(|error: notify::Error| io::Error::other(error.to_string()))?;
63    watcher
64        .watch(&src_path, RecursiveMode::Recursive)
65        .map_err(|error: notify::Error| io::Error::other(error.to_string()))?;
66    log::info!("Watching src/ for changes...");
67    let mut debounce: Interval = interval(Duration::from_millis(500));
68    debounce.tick().await;
69    while rx.changed().await.is_ok() {
70        let event: Event = rx.borrow().clone();
71        let has_rust_change: bool = event
72            .paths
73            .iter()
74            .any(|path: &PathBuf| path.extension().is_some_and(|ext: &OsStr| ext == "rs"));
75        if !has_rust_change {
76            continue;
77        }
78        log::warn!("File change detected: {}", event.paths[0].display());
79        debounce.reset();
80        sleep(Duration::from_millis(300)).await;
81        run_cargo_run().await?;
82    }
83    Ok(())
84}