Skip to main content

sqry_cli/commands/
watch.rs

1//! Watch mode command for real-time index updates
2
3use crate::args::Cli;
4use crate::commands::index::{create_build_config, create_progress_reporter};
5use anyhow::{Context, Result};
6use sqry_core::graph::unified::persistence::GraphStorage;
7use sqry_core::watch::FileWatcher;
8use std::path::PathBuf;
9use std::time::Duration;
10
11/// Execute the watch command.
12///
13/// # Errors
14/// Returns an error if the index cannot be loaded or watch mode fails.
15pub fn execute(
16    cli: &Cli,
17    path: Option<String>,
18    threads: Option<usize>,
19    debounce: Option<u64>,
20    _show_stats: bool, // Detailed stats not supported for unified graph yet
21    build_if_missing: bool,
22) -> Result<()> {
23    let root_path = resolve_path(path)?;
24    let build_config = create_build_config(cli, &root_path, threads)?;
25    let plugins = sqry_plugin_registry::create_plugin_manager();
26
27    // Check if graph exists
28    let storage = GraphStorage::new(&root_path);
29    if !storage.exists() {
30        if build_if_missing {
31            println!("🔨 Building initial graph...");
32            let (_, progress) = create_progress_reporter(cli);
33            sqry_core::graph::unified::build::build_and_persist_graph_with_progress(
34                &root_path,
35                &plugins,
36                &build_config,
37                "cli:watch",
38                progress,
39            )?;
40        } else {
41            anyhow::bail!(
42                "No index found at {}. Use --build to create one, or run 'sqry index' first.",
43                root_path.display()
44            );
45        }
46    }
47
48    // Create watcher
49    let watcher = FileWatcher::new(&root_path)?;
50    let debounce_duration = debounce.map_or(Duration::from_millis(500), Duration::from_millis);
51
52    println!("🔍 Watch mode started");
53    println!("📂 Monitoring: {}", root_path.display());
54    println!("⏱️  Debounce: {}ms", debounce_duration.as_millis());
55    println!();
56    println!("Press Ctrl+C to stop...");
57    println!();
58
59    let (_, progress) = create_progress_reporter(cli);
60
61    loop {
62        // Wait for changes
63        let changes = watcher.wait_with_debounce(debounce_duration)?;
64
65        if changes.is_empty() {
66            continue;
67        }
68
69        println!(
70            "📝 Detected {} file changes, updating graph...",
71            changes.len()
72        );
73        let start = std::time::Instant::now();
74
75        // Full rebuild using consolidated pipeline
76        match sqry_core::graph::unified::build::build_and_persist_graph_with_progress(
77            &root_path,
78            &plugins,
79            &build_config,
80            "cli:watch",
81            progress.clone(),
82        ) {
83            Ok((_graph, _build_result)) => {
84                println!("✓ Graph updated in {:.2}s", start.elapsed().as_secs_f64());
85            }
86            Err(e) => {
87                eprintln!("❌ Error updating graph: {e}");
88            }
89        }
90        println!();
91    }
92}
93
94/// Resolve path argument to absolute `PathBuf`
95fn resolve_path(path: Option<String>) -> Result<PathBuf> {
96    let path_str = path.unwrap_or_else(|| ".".to_string());
97    let path = PathBuf::from(path_str);
98
99    if path.exists() {
100        path.canonicalize().context("Failed to resolve path")
101    } else {
102        anyhow::bail!("Path does not exist: {}", path.display());
103    }
104}