use std::path::Path;
use rusqlite::Connection;
use tracing::debug;
#[allow(dead_code)]
pub struct IndexBridge {
conn: Option<Connection>,
project_id: Option<i64>,
}
impl IndexBridge {
pub fn open(project_root: &Path) -> Self {
let conn = match crate::index::open_global_index() {
Ok(c) => c,
Err(e) => {
debug!(error = %e, "index bridge: global index unavailable");
return Self::unavailable();
}
};
let project_id = match crate::index::ensure_project(&conn, project_root) {
Ok(id) => Some(id),
Err(e) => {
debug!(error = %e, "index bridge: failed to resolve project_id");
return Self::unavailable();
}
};
debug!(project_id, "index bridge: opened successfully");
Self {
conn: Some(conn),
project_id,
}
}
pub fn unavailable() -> Self {
Self {
conn: None,
project_id: None,
}
}
#[inline]
pub fn is_available(&self) -> bool {
self.conn.is_some() && self.project_id.is_some()
}
#[allow(dead_code)]
#[inline]
pub fn project_id(&self) -> Option<i64> {
self.project_id
}
pub fn search_symbols(&self, query: &str, limit: usize) -> Vec<crate::index::SearchResult> {
let conn = match self.conn.as_ref() {
Some(c) => c,
None => return Vec::new(),
};
let pid = match self.project_id {
Some(id) => id,
None => return Vec::new(),
};
let sq = crate::index::SymbolQuery::text(query);
let mut sq = sq;
sq.limit = limit;
crate::index::search(conn, pid, &sq).unwrap_or_default()
}
pub fn find_callers(
&self,
symbol_name: &str,
limit: usize,
) -> Vec<crate::index::graph::CallerResult> {
let conn = match self.conn.as_ref() {
Some(c) => c,
None => return Vec::new(),
};
let pid = match self.project_id {
Some(id) => id,
None => return Vec::new(),
};
crate::index::graph::find_callers(conn, pid, symbol_name, limit).unwrap_or_default()
}
#[allow(dead_code)]
pub fn brain_search(&self, query: &str, limit: usize) -> Vec<crate::index::brain::BrainResult> {
let conn = match self.conn.as_ref() {
Some(c) => c,
None => return Vec::new(),
};
let pid = match self.project_id {
Some(id) => id,
None => return Vec::new(),
};
crate::index::brain::brain_search(conn, pid, query, limit).unwrap_or_default()
}
#[allow(dead_code)]
pub fn impact_analysis(
&self,
symbol_name: &str,
depth: u32,
) -> Vec<crate::index::graph::ImpactNode> {
let conn = match self.conn.as_ref() {
Some(c) => c,
None => return Vec::new(),
};
let pid = match self.project_id {
Some(id) => id,
None => return Vec::new(),
};
crate::index::graph::impact_analysis(conn, pid, symbol_name, depth).unwrap_or_default()
}
#[allow(dead_code)]
#[inline]
pub fn connection(&self) -> Option<&Connection> {
self.conn.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unavailable_bridge_reports_false() {
let bridge = IndexBridge::unavailable();
assert!(!bridge.is_available());
assert_eq!(bridge.project_id(), None);
assert!(bridge.search_symbols("foo", 10).is_empty());
assert!(bridge.find_callers("foo", 10).is_empty());
assert!(bridge.brain_search("foo", 10).is_empty());
assert!(bridge.connection().is_none());
}
#[test]
fn open_nonexistent_project_returns_unavailable() {
let dir = tempfile::tempdir().unwrap();
let bridge = IndexBridge::open(dir.path());
let _ = bridge.is_available();
}
}