use crate::cli::responses::*;
use crate::cli::{resolve_db_path, Cli, OutputFormat, PatternsArgs};
use crate::output;
use anyhow::Result;
pub fn patterns(args: &PatternsArgs, cli: &Cli) -> Result<()> {
use crate::cfg::{detect_if_else_patterns, detect_match_patterns};
use crate::cfg::{load_cfg_from_db, resolve_function_name_with_file};
use crate::storage::MirageDb;
let db_path = resolve_db_path(cli.db.clone())?;
let db = match MirageDb::open(&db_path) {
Ok(db) => db,
Err(_e) => {
if matches!(cli.output, OutputFormat::Json | OutputFormat::Pretty) {
let error = output::JsonError::database_not_found(&db_path);
let wrapper = output::JsonResponse::new(error);
println!("{}", wrapper.to_json());
std::process::exit(output::EXIT_DATABASE);
} else {
output::error(&format!("Failed to open database: {}", db_path));
output::info("Hint: Run 'magellan watch' to create the database");
std::process::exit(output::EXIT_DATABASE);
}
}
};
let function_id =
match resolve_function_name_with_file(&db, &args.function, args.file.as_deref()) {
Ok(id) => id,
Err(_e) => {
if matches!(cli.output, OutputFormat::Json | OutputFormat::Pretty) {
let error = output::JsonError::function_not_found(&args.function);
let wrapper = output::JsonResponse::new(error);
println!("{}", wrapper.to_json());
std::process::exit(output::EXIT_DATABASE);
} else {
output::error(&format!(
"Function '{}' not found in database",
args.function
));
output::info(&format!("Hint: {}", output::R_HINT_LIST_FUNCTIONS));
std::process::exit(output::EXIT_DATABASE);
}
}
};
let cfg = match load_cfg_from_db(&db, function_id) {
Ok(cfg) => cfg,
Err(_e) => {
if matches!(cli.output, OutputFormat::Json | OutputFormat::Pretty) {
let error = output::JsonError::new(
"CgfLoadError",
&format!("Failed to load CFG for function '{}'", args.function),
output::E_CFG_ERROR,
);
let wrapper = output::JsonResponse::new(error);
println!("{}", wrapper.to_json());
std::process::exit(output::EXIT_DATABASE);
} else {
output::error(&format!(
"Failed to load CFG for function '{}'",
args.function
));
output::info("The function may be corrupted. Try re-running 'magellan watch'");
std::process::exit(output::EXIT_DATABASE);
}
}
};
let show_if_else = !args.r#match; let show_match = !args.if_else;
let if_else_patterns = if show_if_else {
detect_if_else_patterns(&cfg)
} else {
vec![]
};
let match_patterns = if show_match {
detect_match_patterns(&cfg)
} else {
vec![]
};
let if_else_infos: Vec<IfElseInfo> = if_else_patterns
.iter()
.map(|p| IfElseInfo {
condition_block: cfg[p.condition].id,
true_branch: cfg[p.true_branch].id,
false_branch: cfg[p.false_branch].id,
merge_point: p.merge_point.map(|n| cfg[n].id),
has_else: p.has_else(),
})
.collect();
let match_infos: Vec<MatchInfo> = match_patterns
.iter()
.map(|p| MatchInfo {
switch_block: cfg[p.switch_node].id,
branch_count: p.branch_count(),
targets: p.targets.iter().map(|n| cfg[*n].id).collect(),
otherwise: cfg[p.otherwise].id,
})
.collect();
match cli.output {
OutputFormat::Human => {
println!("Function: {}", args.function);
println!();
if show_if_else {
println!("If/Else Patterns: {}", if_else_patterns.len());
if if_else_patterns.is_empty() {
output::info("No if/else patterns detected");
} else {
for (i, info) in if_else_infos.iter().enumerate() {
println!(" Pattern {}:", i + 1);
println!(" Condition: Block {}", info.condition_block);
println!(" True branch: Block {}", info.true_branch);
println!(" False branch: Block {}", info.false_branch);
if let Some(merge) = info.merge_point {
println!(" Merge point: Block {}", merge);
println!(" Has else: {}", info.has_else);
} else {
println!(" Merge point: None (no else)");
}
println!();
}
}
println!();
}
if show_match {
println!("Match Patterns: {}", match_patterns.len());
if match_patterns.is_empty() {
output::info("No match patterns detected");
} else {
for (i, info) in match_infos.iter().enumerate() {
println!(" Pattern {}:", i + 1);
println!(" Switch: Block {}", info.switch_block);
println!(" Branch count: {}", info.branch_count);
println!(" Targets: {:?}", info.targets);
println!(" Otherwise: Block {}", info.otherwise);
println!();
}
}
}
}
OutputFormat::Json | OutputFormat::Pretty => {
let response = PatternsResponse {
function: args.function.clone(),
if_else_count: if_else_patterns.len(),
match_count: match_patterns.len(),
if_else_patterns: if_else_infos,
match_patterns: match_infos,
};
let wrapper = output::JsonResponse::new(response);
match cli.output {
OutputFormat::Json => println!("{}", wrapper.to_json()),
OutputFormat::Pretty => println!("{}", wrapper.to_pretty_json()),
_ => unreachable!(),
}
}
}
Ok(())
}