use anyhow::Result;
use magellan::graph::DeadSymbol;
use magellan::output::{output_json, JsonResponse, OutputFormat};
use magellan::CodeGraph;
use std::path::PathBuf;
pub fn run_dead_code(
db_path: PathBuf,
entry_symbol_id: String,
output_format: OutputFormat,
) -> Result<()> {
let args = vec![
"dead-code".to_string(),
"--entry".to_string(),
entry_symbol_id.clone(),
];
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 dead_symbols = graph.dead_symbols(&entry_symbol_id)?;
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(&entry_symbol_id, dead_symbols, &exec_id, output_format);
}
if dead_symbols.is_empty() {
println!(
"No dead code found. All symbols are reachable from \"{}\"",
entry_symbol_id
);
} else {
println!("Dead code (unreachable from \"{}\"):", entry_symbol_id);
for dead in &dead_symbols {
let fqn_display = dead.symbol.fqn.as_deref().unwrap_or("?");
println!(
" {} ({}) in {} - {}",
fqn_display, dead.symbol.kind, dead.symbol.file_path, dead.reason
);
}
}
graph
.execution_log()
.finish_execution(&exec_id, "success", None, 0, 0, 0)?;
Ok(())
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct DeadCodeResponse {
pub entry_symbol_id: String,
pub count: usize,
pub dead_symbols: Vec<DeadSymbolJson>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct DeadSymbolJson {
pub symbol: SymbolInfoJson,
pub reason: String,
}
#[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<DeadSymbol> for DeadSymbolJson {
fn from(dead: DeadSymbol) -> Self {
Self {
symbol: SymbolInfoJson::from(dead.symbol),
reason: dead.reason,
}
}
}
impl From<magellan::graph::SymbolInfo> for SymbolInfoJson {
fn from(info: magellan::graph::SymbolInfo) -> Self {
Self {
symbol_id: info.symbol_id,
fqn: info.fqn,
file_path: info.file_path,
kind: info.kind,
}
}
}
fn output_json_mode(
entry_symbol_id: &str,
dead_symbols: Vec<DeadSymbol>,
exec_id: &str,
output_format: OutputFormat,
) -> Result<()> {
let dead_symbols_json: Vec<DeadSymbolJson> =
dead_symbols.into_iter().map(DeadSymbolJson::from).collect();
let response = DeadCodeResponse {
entry_symbol_id: entry_symbol_id.to_string(),
count: dead_symbols_json.len(),
dead_symbols: dead_symbols_json,
};
let json_response = JsonResponse::new(response, exec_id);
output_json(&json_response, output_format)?;
Ok(())
}