Skip to main content

ix/
lib.rs

1// ix — trigram code search library
2//
3// Build order: format → varint → trigram → bloom → posting →
4//              string_pool → builder → reader → planner → executor → scanner
5
6pub mod bloom;
7pub mod builder;
8pub mod config;
9pub mod decompress;
10pub mod archive;
11pub mod error;
12pub mod executor;
13pub mod format;
14#[cfg(feature = "notify")]
15pub mod idle;
16pub mod planner;
17pub mod posting;
18pub mod reader;
19pub mod scanner;
20pub mod string_pool;
21pub mod trigram;
22pub mod varint;
23#[cfg(feature = "notify")]
24pub mod watcher;
25
26#[cfg(feature = "notify")]
27pub use crate::watcher::Watcher;
28#[cfg(feature = "notify")]
29pub use crate::idle::IdleTracker;
30#[cfg(feature = "notify")]
31pub use crate::builder::Builder;
32#[cfg(feature = "notify")]
33pub use crate::format::Beacon;
34
35#[cfg(feature = "notify")]
36pub fn run_daemon(path: &std::path::Path) -> crate::error::Result<()> {
37    use std::fs;
38    use std::sync::Arc;
39    use std::sync::atomic::{AtomicBool, Ordering};
40    use std::time::Duration;
41
42    let root = path.canonicalize().map_err(crate::error::Error::Io)?;
43
44    println!("ixd: watching {}...", root.display());
45
46    let mut builder = Builder::new(&root);
47    builder.build()?;
48    println!(
49        "ixd: initial build complete ({} files, {} trigrams)",
50        builder.files_len(),
51        builder.trigrams_len()
52    );
53
54    let mut watcher = Watcher::new(&root)?;
55    let rx = watcher.start()?;
56
57    let ix_dir = root.join(".ix");
58    if !ix_dir.exists() {
59        fs::create_dir_all(&ix_dir)?;
60    }
61    let mut beacon = Beacon::new(&root);
62    beacon.write_to(&ix_dir)?;
63
64    let mut idle = IdleTracker::new();
65
66    let running = Arc::new(AtomicBool::new(true));
67    let r = running.clone();
68    ctrlc::set_handler(move || {
69        r.store(false, Ordering::SeqCst);
70    })
71    .expect("Error setting Ctrl-C handler");
72
73    while running.load(Ordering::SeqCst) {
74        match rx.recv_timeout(Duration::from_secs(5)) {
75            Ok(changed_files) => {
76                println!(
77                    "ixd: {} files changed, updating index...",
78                    changed_files.len()
79                );
80
81                beacon.status = "indexing".to_string();
82                beacon.last_event_at = std::time::SystemTime::now()
83                    .duration_since(std::time::UNIX_EPOCH)
84                    .unwrap_or_default()
85                    .as_secs();
86                let _ = beacon.write_to(&ix_dir);
87
88                idle.record_change();
89                builder.update(&changed_files)?;
90
91                beacon.status = "idle".to_string();
92                let _ = beacon.write_to(&ix_dir);
93            }
94            Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
95                continue;
96            }
97            Err(crossbeam_channel::RecvTimeoutError::Disconnected) => break,
98        }
99    }
100
101    println!("ixd: shutting down");
102    let _ = fs::remove_file(ix_dir.join("beacon.json"));
103    watcher.stop();
104    Ok(())
105}