use anyhow::Result;
use magellan::graph::SymbolInfo;
use magellan::output::{output_json, JsonResponse, OutputFormat};
use magellan::CodeGraph;
use std::path::PathBuf;
pub fn run_reachable(
db_path: PathBuf,
symbol_id: String,
reverse: bool,
output_format: OutputFormat,
) -> Result<()> {
let mut args = vec!["reachable".to_string()];
args.push("--symbol".to_string());
args.push(symbol_id.clone());
if reverse {
args.push("--reverse".to_string());
}
let graph = CodeGraph::open(&db_path)?;
let exec_id = magellan::output::generate_execution_id();
let db_path_str = db_path.to_string_lossy().to_string();
graph.execution_log().start_execution(
&exec_id,
env!("CARGO_PKG_VERSION"),
&args,
None,
&db_path_str,
)?;
let symbols = if reverse {
graph.reverse_reachable_symbols(&symbol_id, None)?
} else {
graph.reachable_symbols(&symbol_id, None)?
};
if output_format == OutputFormat::Json || output_format == OutputFormat::Pretty {
graph
.execution_log()
.finish_execution(&exec_id, "success", None, 0, 0, 0)?;
return output_json_mode(&symbol_id, reverse, symbols, &exec_id, output_format);
}
let direction_label = if reverse {
"that can reach"
} else {
"reachable from"
};
if symbols.is_empty() {
println!("No symbols {} \"{}\"", direction_label, symbol_id);
} else {
println!("Symbols {} \"{}\":", direction_label, symbol_id);
for symbol in &symbols {
let fqn_display = symbol.fqn.as_deref().unwrap_or("?");
println!(
" {} ({}) in {}",
fqn_display, symbol.kind, symbol.file_path
);
}
}
graph
.execution_log()
.finish_execution(&exec_id, "success", None, 0, 0, 0)?;
Ok(())
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct ReachableResponse {
pub symbol_id: String,
pub direction: String,
pub count: usize,
pub symbols: Vec<SymbolInfoJson>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct SymbolInfoJson {
pub symbol_id: Option<String>,
pub fqn: Option<String>,
pub file_path: String,
pub kind: String,
}
impl From<SymbolInfo> for SymbolInfoJson {
fn from(info: SymbolInfo) -> Self {
Self {
symbol_id: info.symbol_id,
fqn: info.fqn,
file_path: info.file_path,
kind: info.kind,
}
}
}
fn output_json_mode(
symbol_id: &str,
reverse: bool,
symbols: Vec<SymbolInfo>,
exec_id: &str,
output_format: OutputFormat,
) -> Result<()> {
let direction = if reverse { "reverse" } else { "forward" }.to_string();
let symbols_json: Vec<SymbolInfoJson> = symbols.into_iter().map(SymbolInfoJson::from).collect();
let response = ReachableResponse {
symbol_id: symbol_id.to_string(),
direction,
count: symbols_json.len(),
symbols: symbols_json,
};
let json_response = JsonResponse::new(response, exec_id);
output_json(&json_response, output_format)?;
Ok(())
}