Skip to main content

harn_hostlib/code_index/
mod.rs

1//! Code index host capability.
2//!
3//! Ports the deterministic trigram/word index that lived in
4//! `Sources/BurinCodeIndex/` on the Swift side. Exposes five host builtins
5//! whose surfaces are locked by `schemas/code_index/<method>.json`:
6//!
7//! | Builtin                          | What it does                                           |
8//! |----------------------------------|--------------------------------------------------------|
9//! | `hostlib_code_index_query`       | Trigram-accelerated literal substring search.          |
10//! | `hostlib_code_index_rebuild`     | Walk a workspace and (re)build the in-memory index.    |
11//! | `hostlib_code_index_stats`       | Count files/trigrams/words + last rebuild timestamp.   |
12//! | `hostlib_code_index_imports_for` | Imports declared by a single file (with resolutions).  |
13//! | `hostlib_code_index_importers_of`| Reverse lookup: who imports the given module/path?     |
14//!
15//! The capability owns one [`SharedIndex`] cell per instance — there is at
16//! most one live workspace per VM at a time. The schema for the read
17//! builtins (`query`, `stats`, `imports_for`, `importers_of`) does not
18//! carry a workspace argument, so multi-workspace embedders are expected
19//! to drive separate `CodeIndexCapability` handles. The internal layout
20//! still keeps everything keyed on `FileId`, so a future schema bump can
21//! add a `root` parameter without refactoring the data model.
22
23mod builtins;
24mod file_table;
25mod graph;
26mod imports;
27mod state;
28mod trigram;
29mod walker;
30mod words;
31
32use std::sync::{Arc, Mutex};
33
34use harn_vm::VmValue;
35
36use crate::error::HostlibError;
37use crate::registry::{BuiltinRegistry, HostlibCapability, RegisteredBuiltin, SyncHandler};
38
39pub use builtins::SharedIndex;
40pub use file_table::{FileId, IndexedFile, IndexedSymbol};
41pub use graph::DepGraph;
42pub use state::{BuildOutcome, IndexState};
43pub use trigram::TrigramIndex;
44pub use words::{WordHit, WordIndex};
45
46/// Code-index capability handle.
47///
48/// Holds the [`SharedIndex`] cell behind an `Arc<Mutex<...>>`; cloning the
49/// capability shares state. Embedders that want isolated workspaces should
50/// construct independent instances.
51#[derive(Clone, Default)]
52pub struct CodeIndexCapability {
53    index: SharedIndex,
54}
55
56impl CodeIndexCapability {
57    /// Create a capability with an empty workspace slot. The first
58    /// `hostlib_code_index_rebuild` call populates it.
59    pub fn new() -> Self {
60        Self {
61            index: Arc::new(Mutex::new(None)),
62        }
63    }
64
65    /// Borrow the underlying shared cell. Useful for tests and embedders
66    /// that want to introspect index state without going through the
67    /// builtins.
68    pub fn shared(&self) -> SharedIndex {
69        self.index.clone()
70    }
71}
72
73impl HostlibCapability for CodeIndexCapability {
74    fn module_name(&self) -> &'static str {
75        "code_index"
76    }
77
78    fn register_builtins(&self, registry: &mut BuiltinRegistry) {
79        register(
80            registry,
81            self.index.clone(),
82            builtins::BUILTIN_QUERY,
83            "query",
84            builtins::run_query,
85        );
86        register(
87            registry,
88            self.index.clone(),
89            builtins::BUILTIN_REBUILD,
90            "rebuild",
91            builtins::run_rebuild,
92        );
93        register(
94            registry,
95            self.index.clone(),
96            builtins::BUILTIN_STATS,
97            "stats",
98            builtins::run_stats,
99        );
100        register(
101            registry,
102            self.index.clone(),
103            builtins::BUILTIN_IMPORTS_FOR,
104            "imports_for",
105            builtins::run_imports_for,
106        );
107        register(
108            registry,
109            self.index.clone(),
110            builtins::BUILTIN_IMPORTERS_OF,
111            "importers_of",
112            builtins::run_importers_of,
113        );
114    }
115}
116
117fn register(
118    registry: &mut BuiltinRegistry,
119    index: SharedIndex,
120    name: &'static str,
121    method: &'static str,
122    runner: fn(&SharedIndex, &[VmValue]) -> Result<VmValue, HostlibError>,
123) {
124    let captured = index;
125    let handler: SyncHandler = Arc::new(move |args| runner(&captured, args));
126    registry.register(RegisteredBuiltin {
127        name,
128        module: "code_index",
129        method,
130        handler,
131    });
132}