mod agents;
mod builtins;
mod file_table;
mod graph;
mod imports;
mod snapshot;
mod state;
mod trigram;
mod versions;
mod walker;
mod words;
use std::path::Path;
use std::sync::{Arc, Mutex};
use harn_vm::VmValue;
use crate::error::HostlibError;
use crate::registry::{BuiltinRegistry, HostlibCapability, RegisteredBuiltin, SyncHandler};
pub use agents::{AgentId, AgentInfo, AgentRegistry, AgentState, RegistryConfig};
pub use builtins::SharedIndex;
pub use file_table::{FileId, IndexedFile, IndexedSymbol};
pub use graph::DepGraph;
pub use snapshot::{CodeIndexSnapshot, SnapshotMeta};
pub use state::{BuildOutcome, IndexState};
pub use trigram::TrigramIndex;
pub use versions::{ChangeRecord, EditOp, VersionEntry, VersionLog, HISTORY_LIMIT};
pub use words::{WordHit, WordIndex};
#[derive(Clone, Default)]
pub struct CodeIndexCapability {
index: SharedIndex,
current_agent: Arc<Mutex<Option<AgentId>>>,
}
impl CodeIndexCapability {
pub fn new() -> Self {
Self {
index: Arc::new(Mutex::new(None)),
current_agent: Arc::new(Mutex::new(None)),
}
}
pub fn shared(&self) -> SharedIndex {
self.index.clone()
}
pub fn current_agent_slot(&self) -> Arc<Mutex<Option<AgentId>>> {
self.current_agent.clone()
}
pub fn set_current_agent(&self, id: Option<AgentId>) -> Option<AgentId> {
let mut guard = self.current_agent.lock().expect("current_agent poisoned");
std::mem::replace(&mut *guard, id)
}
pub fn restore_from_disk(&self, workspace_root: &Path) -> std::io::Result<bool> {
match CodeIndexSnapshot::load(workspace_root)? {
Some(snap) => {
let mut state = IndexState::from_snapshot(snap);
state.reap_after_recovery(state::now_unix_ms());
let mut guard = self.index.lock().expect("code_index mutex poisoned");
*guard = Some(state);
Ok(true)
}
None => Ok(false),
}
}
pub fn persist_to_disk(&self) -> std::io::Result<bool> {
let snap = {
let guard = self.index.lock().expect("code_index mutex poisoned");
guard
.as_ref()
.map(|state| (state.snapshot(), state.root.clone()))
};
match snap {
Some((snap, root)) => {
snap.save(&root)?;
Ok(true)
}
None => Ok(false),
}
}
}
impl HostlibCapability for CodeIndexCapability {
fn module_name(&self) -> &'static str {
"code_index"
}
fn register_builtins(&self, registry: &mut BuiltinRegistry) {
register(
registry,
self.index.clone(),
builtins::BUILTIN_QUERY,
"query",
builtins::run_query,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_REBUILD,
"rebuild",
builtins::run_rebuild,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_STATS,
"stats",
builtins::run_stats,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_IMPORTS_FOR,
"imports_for",
builtins::run_imports_for,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_IMPORTERS_OF,
"importers_of",
builtins::run_importers_of,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_PATH_TO_ID,
"path_to_id",
builtins::run_path_to_id,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_ID_TO_PATH,
"id_to_path",
builtins::run_id_to_path,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_FILE_IDS,
"file_ids",
builtins::run_file_ids,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_FILE_META,
"file_meta",
builtins::run_file_meta,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_FILE_HASH,
"file_hash",
builtins::run_file_hash,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_READ_RANGE,
"read_range",
builtins::run_read_range,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_REINDEX_FILE,
"reindex_file",
builtins::run_reindex_file,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_TRIGRAM_QUERY,
"trigram_query",
builtins::run_trigram_query,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_EXTRACT_TRIGRAMS,
"extract_trigrams",
builtins::run_extract_trigrams,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_WORD_GET,
"word_get",
builtins::run_word_get,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_DEPS_GET,
"deps_get",
builtins::run_deps_get,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_OUTLINE_GET,
"outline_get",
builtins::run_outline_get,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_CURRENT_SEQ,
"current_seq",
builtins::run_current_seq,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_CHANGES_SINCE,
"changes_since",
builtins::run_changes_since,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_VERSION_RECORD,
"version_record",
builtins::run_version_record,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_AGENT_REGISTER,
"agent_register",
builtins::run_agent_register,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_AGENT_HEARTBEAT,
"agent_heartbeat",
builtins::run_agent_heartbeat,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_AGENT_UNREGISTER,
"agent_unregister",
builtins::run_agent_unregister,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_LOCK_TRY,
"lock_try",
builtins::run_lock_try,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_LOCK_RELEASE,
"lock_release",
builtins::run_lock_release,
);
register(
registry,
self.index.clone(),
builtins::BUILTIN_STATUS,
"status",
builtins::run_status,
);
let slot = self.current_agent.clone();
let handler: SyncHandler =
Arc::new(move |args| builtins::run_current_agent_id(&slot, args));
registry.register(RegisteredBuiltin {
name: builtins::BUILTIN_CURRENT_AGENT_ID,
module: "code_index",
method: "current_agent_id",
handler,
});
}
}
fn register(
registry: &mut BuiltinRegistry,
index: SharedIndex,
name: &'static str,
method: &'static str,
runner: fn(&SharedIndex, &[VmValue]) -> Result<VmValue, HostlibError>,
) {
let captured = index;
let handler: SyncHandler = Arc::new(move |args| runner(&captured, args));
registry.register(RegisteredBuiltin {
name,
module: "code_index",
method,
handler,
});
}