pub mod ast;
use std::path::{Path, PathBuf};
pub use ast::{analyze_file, get_call_graph, syntax_check};
pub struct ToolDef {
pub name: &'static str,
pub description: &'static str,
pub parameters_json: &'static str,
}
pub fn tool_definitions() -> Vec<ToolDef> {
vec![ToolDef {
name: "AstAnalysis",
description: "Read-only AST code analysis. Use 'analyze_file' for functions/classes/structs \
summary, or 'get_call_graph' with a symbol name to find callers and callees. \
Supports .rs, .py, .pyi, .pyw, .js, .jsx, .mjs, .cjs, .ts, .mts, .cts, .tsx, \
.go, .java, .c, .h, .cpp, .cc, .cxx, .hpp, .hh, .sh, .bash files.",
parameters_json: r#"{"type":"object","properties":{"action":{"type":"string","description":"'analyze_file' or 'get_call_graph'"},"file_path":{"type":"string","description":"Path to file"},"symbol":{"type":"string","description":"Symbol for get_call_graph"}},"required":["action","file_path"]}"#,
}]
}
pub fn execute(
project_root: &Path,
action: &str,
file_path: &str,
symbol: Option<&str>,
) -> Result<String, String> {
let path = if Path::new(file_path).is_absolute() {
PathBuf::from(file_path)
} else {
project_root.join(file_path)
};
if !path.exists() {
return Err(format!("File not found: {file_path}"));
}
match action {
"analyze_file" => analyze_file(&path).map_err(|e| e.to_string()),
"get_call_graph" => {
let sym = symbol.unwrap_or("");
if sym.is_empty() {
return Err("'symbol' is required for get_call_graph".to_string());
}
get_call_graph(&path, sym).map_err(|e| e.to_string())
}
other => Err(format!(
"Unknown action '{other}'. Use 'analyze_file' or 'get_call_graph'."
)),
}
}