use anyhow::Result;
use super::super::BatchContext;
pub(in crate::cli::batch) fn dispatch_deps(
ctx: &BatchContext,
name: &str,
reverse: bool,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_deps", name, reverse, cross_project).entered();
if cross_project {
tracing::warn!("cross-project deps not yet supported, returning local result");
}
if reverse {
let types = ctx.store().get_types_used_by(name)?;
let output = crate::cli::commands::build_deps_reverse(name, &types);
Ok(serde_json::to_value(&output)?)
} else {
let users = ctx.store().get_type_users(name)?;
let output = crate::cli::commands::build_deps_forward(&users, &ctx.root);
Ok(serde_json::to_value(&output)?)
}
}
pub(in crate::cli::batch) fn dispatch_callers(
ctx: &BatchContext,
name: &str,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_callers", name, cross_project).entered();
if cross_project {
let mut cross_ctx = cqs::cross_project::CrossProjectContext::from_config(&ctx.root)?;
let callers = cross_ctx.get_callers_cross(name)?;
return Ok(serde_json::to_value(&callers)?);
}
let callers = ctx.store().get_callers_full(name)?;
let output = crate::cli::commands::build_callers(&callers);
Ok(serde_json::to_value(&output)?)
}
pub(in crate::cli::batch) fn dispatch_callees(
ctx: &BatchContext,
name: &str,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_callees", name, cross_project).entered();
if cross_project {
let mut cross_ctx = cqs::cross_project::CrossProjectContext::from_config(&ctx.root)?;
let callees = cross_ctx.get_callees_cross(name)?;
return Ok(serde_json::to_value(&callees)?);
}
let callees = ctx.store().get_callees_full(name, None)?;
let output = crate::cli::commands::build_callees(name, &callees);
Ok(serde_json::to_value(&output)?)
}
pub(in crate::cli::batch) fn dispatch_impact(
ctx: &BatchContext,
name: &str,
depth: usize,
do_suggest_tests: bool,
include_types: bool,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_impact", name, cross_project).entered();
let depth = depth.clamp(1, 10);
if cross_project {
let mut cross_ctx = cqs::cross_project::CrossProjectContext::from_config(&ctx.root)?;
let result = cqs::cross_project::analyze_impact_cross(
&mut cross_ctx,
name,
depth,
do_suggest_tests,
include_types,
)?;
let json = cqs::impact_to_json(&result);
return Ok(json);
}
let resolved = cqs::resolve_target(&ctx.store(), name)?;
let chunk = &resolved.chunk;
let result = cqs::analyze_impact(
&ctx.store(),
&chunk.name,
&ctx.root,
&cqs::ImpactOptions {
depth,
include_types,
},
)?;
let mut json = cqs::impact_to_json(&result);
if do_suggest_tests {
let suggestions = cqs::suggest_tests(&ctx.store(), &result, &ctx.root);
let suggestions_json = cqs::format_test_suggestions(&suggestions);
if let Some(obj) = json.as_object_mut() {
obj.insert(
"test_suggestions".into(),
serde_json::json!(suggestions_json),
);
}
}
Ok(json)
}
pub(in crate::cli::batch) fn dispatch_test_map(
ctx: &BatchContext,
name: &str,
max_depth: usize,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_test_map", name, cross_project).entered();
if cross_project {
let mut cross_ctx = cqs::cross_project::CrossProjectContext::from_config(&ctx.root)?;
let test_chunks = cross_ctx.find_test_chunks_cross()?;
let graph = cross_ctx.merged_call_graph()?;
let summaries: Vec<cqs::store::ChunkSummary> =
test_chunks.iter().map(|tc| tc.chunk.clone()).collect();
let matches =
crate::cli::commands::build_test_map(name, &graph, &summaries, &ctx.root, max_depth);
let output = crate::cli::commands::build_test_map_output(name, &matches);
return Ok(serde_json::to_value(&output)?);
}
let resolved = cqs::resolve_target(&ctx.store(), name)?;
let target_name = resolved.chunk.name.clone();
let graph = ctx.call_graph()?;
let test_chunks = ctx.store().find_test_chunks()?;
let matches = crate::cli::commands::build_test_map(
&target_name,
&graph,
&test_chunks,
&ctx.root,
max_depth,
);
let output = crate::cli::commands::build_test_map_output(&target_name, &matches);
Ok(serde_json::to_value(&output)?)
}
pub(in crate::cli::batch) fn dispatch_trace(
ctx: &BatchContext,
source: &str,
target: &str,
max_depth: usize,
cross_project: bool,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_trace", source, target, cross_project).entered();
if cross_project {
let mut cross_ctx = cqs::cross_project::CrossProjectContext::from_config(&ctx.root)?;
let result = cqs::cross_project::trace_cross(&mut cross_ctx, source, target, max_depth)?;
let trace_result = cqs::cross_project::CrossProjectTraceResult {
source: source.to_string(),
target: target.to_string(),
depth: result.as_ref().map(|p| p.len().saturating_sub(1)),
found: result.is_some(),
path: result,
};
return Ok(serde_json::to_value(&trace_result)?);
}
let source_resolved = cqs::resolve_target(&ctx.store(), source)?;
let target_resolved = cqs::resolve_target(&ctx.store(), target)?;
let source_name = source_resolved.chunk.name.clone();
let target_name = target_resolved.chunk.name.clone();
if source_name == target_name {
let trivial_path = vec![source_name.clone()];
let output = crate::cli::commands::trace::build_trace_output(
&ctx.store(),
&source_name,
&target_name,
Some(&trivial_path),
&ctx.root,
)?;
return Ok(serde_json::to_value(&output)?);
}
let graph = ctx.call_graph()?;
let found_path = crate::cli::commands::trace::bfs_shortest_path(
&graph.forward,
&source_name,
&target_name,
max_depth,
);
let output = crate::cli::commands::trace::build_trace_output(
&ctx.store(),
&source_name,
&target_name,
found_path.as_deref(),
&ctx.root,
)?;
Ok(serde_json::to_value(&output)?)
}
pub(in crate::cli::batch) fn dispatch_related(
ctx: &BatchContext,
name: &str,
limit: usize,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_related", name).entered();
let limit = limit.clamp(1, 100);
let result = cqs::find_related(&ctx.store(), name, limit)?;
let output = crate::cli::commands::build_related_output(&result, &ctx.root);
Ok(serde_json::to_value(&output)?)
}
pub(in crate::cli::batch) fn dispatch_impact_diff(
ctx: &BatchContext,
base: Option<&str>,
) -> Result<serde_json::Value> {
let _span = tracing::info_span!("batch_impact_diff", ?base).entered();
let diff_text = crate::cli::commands::run_git_diff(base)?;
let hunks = cqs::parse_unified_diff(&diff_text);
if hunks.is_empty() {
return Ok(serde_json::json!({
"changed_functions": [],
"callers": [],
"tests": [],
"summary": { "changed_count": 0, "caller_count": 0, "test_count": 0 }
}));
}
let changed = cqs::map_hunks_to_functions(&ctx.store(), &hunks);
if changed.is_empty() {
return Ok(serde_json::json!({
"changed_functions": [],
"callers": [],
"tests": [],
"summary": { "changed_count": 0, "caller_count": 0, "test_count": 0 }
}));
}
let result = cqs::analyze_diff_impact(&ctx.store(), changed, &ctx.root)?;
Ok(cqs::diff_impact_to_json(&result))
}