use super::graph::RelatedChunk;
use crate::db::{ImportDirection, Store};
use anyhow::Result;
async fn related_via_incoming_edge(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
edge_type: &str,
depth: i32,
) -> Result<Vec<RelatedChunk>> {
let edges = store
.get_direct_edges(chunk_id, ImportDirection::Incoming)
.await?;
let mut out = Vec::new();
for e in edges.into_iter().filter(|e| e.edge_type == edge_type) {
if let Some(chunk) = store.get_chunk_by_id(e.chunk_id).await? {
out.push(RelatedChunk {
id: chunk.id,
relpath: chunk.file_path,
symbol_name: chunk.symbol_name,
kind: chunk.kind,
start_line: chunk.start_line,
end_line: chunk.end_line,
preview: chunk.preview,
depth,
relevance: 1.0,
});
}
}
Ok(out)
}
pub async fn find_test_files(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
) -> Result<Vec<RelatedChunk>> {
related_via_incoming_edge(store, chunk_id, "test_of", 0).await
}
pub async fn find_callers(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
max_depth: i32,
) -> Result<Vec<RelatedChunk>> {
let graph_results = store
.find_callers(chunk_id, Some(max_depth as usize))
.await?;
let mut related_chunks = Vec::new();
for result in graph_results {
if let Some(chunk) = store.get_chunk_by_id(result.chunk_id).await? {
related_chunks.push(RelatedChunk {
id: chunk.id,
relpath: chunk.file_path,
symbol_name: chunk.symbol_name,
kind: chunk.kind,
start_line: chunk.start_line,
end_line: chunk.end_line,
preview: chunk.preview,
depth: result.depth as i32,
relevance: 0.7_f64.powi(result.depth as i32), });
}
}
Ok(related_chunks)
}
pub async fn find_callees(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
max_depth: i32,
) -> Result<Vec<RelatedChunk>> {
let graph_results = store
.find_callees(chunk_id, Some(max_depth as usize))
.await?;
let mut related_chunks = Vec::new();
for result in graph_results {
if let Some(chunk) = store.get_chunk_by_id(result.chunk_id).await? {
related_chunks.push(RelatedChunk {
id: chunk.id,
relpath: chunk.file_path,
symbol_name: chunk.symbol_name,
kind: chunk.kind,
start_line: chunk.start_line,
end_line: chunk.end_line,
preview: chunk.preview,
depth: result.depth as i32,
relevance: 0.7_f64.powi(result.depth as i32), });
}
}
Ok(related_chunks)
}
pub async fn find_imports(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
) -> Result<Vec<RelatedChunk>> {
let graph_results = store
.find_imports(chunk_id, ImportDirection::Outgoing, Some(1))
.await?;
let mut related_chunks = Vec::new();
for result in graph_results {
if let Some(chunk) = store.get_chunk_by_id(result.chunk_id).await? {
related_chunks.push(RelatedChunk {
id: chunk.id,
relpath: chunk.file_path,
symbol_name: chunk.symbol_name,
kind: chunk.kind,
start_line: chunk.start_line,
end_line: chunk.end_line,
preview: chunk.preview,
depth: result.depth as i32,
relevance: 1.0, });
}
}
Ok(related_chunks)
}
pub async fn find_exports(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
) -> Result<Vec<RelatedChunk>> {
related_via_incoming_edge(store, chunk_id, "exports", 1).await
}
pub async fn find_routes(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
) -> Result<Vec<RelatedChunk>> {
related_via_incoming_edge(store, chunk_id, "route_of", 1).await
}
pub async fn find_all_relationships(
store: &(dyn Store + Send + Sync),
chunk_id: i64,
max_depth: i32,
) -> Result<(
Vec<RelatedChunk>, // tests
Vec<RelatedChunk>, // callers
Vec<RelatedChunk>, // callees
Vec<RelatedChunk>, // imports
Vec<RelatedChunk>, // exports
Vec<RelatedChunk>, // routes
)> {
let (tests, callers, callees, imports, exports, routes) = tokio::try_join!(
find_test_files(store, chunk_id),
find_callers(store, chunk_id, max_depth),
find_callees(store, chunk_id, max_depth),
find_imports(store, chunk_id),
find_exports(store, chunk_id),
find_routes(store, chunk_id),
)?;
Ok((tests, callers, callees, imports, exports, routes))
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[tokio::test]
#[ignore] async fn test_find_test_files() {
}
#[tokio::test]
#[ignore]
async fn test_find_callers() {
}
#[tokio::test]
#[ignore]
async fn test_find_callees() {
}
}