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}