Skip to main content

hyperlane_cli/watch/
fn.rs

1use crate::*;
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: std::process::Output = Command::new("cargo")
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(
53            "src directory not found in current directory",
54        ));
55    }
56    run_cargo_run().await?;
57    let (tx, mut rx): (Sender<Event>, Receiver<Event>) = channel(Event::new(EventKind::Any));
58    let mut watcher: RecommendedWatcher =
59        recommended_watcher(move |result: Result<Event, notify::Error>| {
60            if let Ok(event) = result {
61                let _ = tx.send(event);
62            }
63        })
64        .map_err(|error: notify::Error| io::Error::other(error.to_string()))?;
65    watcher
66        .watch(&src_path, RecursiveMode::Recursive)
67        .map_err(|error: notify::Error| io::Error::other(error.to_string()))?;
68    log::info!("Watching src/ for changes...");
69    let mut debounce: Interval = interval(Duration::from_millis(500));
70    debounce.tick().await;
71    while rx.changed().await.is_ok() {
72        let event: Event = rx.borrow().clone();
73        let has_rust_change: bool = event.paths.iter().any(|path: &PathBuf| {
74            path.extension()
75                .is_some_and(|ext: &std::ffi::OsStr| ext == "rs")
76        });
77        if !has_rust_change {
78            continue;
79        }
80        log::warn!("File change detected: {}", event.paths[0].display());
81        debounce.reset();
82        sleep(Duration::from_millis(300)).await;
83        run_cargo_run().await?;
84    }
85    Ok(())
86}