use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NameMatch {
pub name: String,
pub file: String,
pub line: usize,
pub kind: String,
pub similarity_score: f32,
pub edit_distance: usize,
pub phonetic_match: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct NameSimilarityResult {
pub query: String,
pub matches: Vec<NameMatch>,
pub total_candidates: usize,
pub search_scope: String,
}
#[allow(clippy::too_many_arguments)]
pub async fn handle_analyze_name_similarity(
project_path: PathBuf,
query: String,
top_k: usize,
phonetic: bool,
scope: crate::cli::SearchScope,
threshold: f32,
format: crate::cli::NameSimilarityOutputFormat,
include: Option<String>,
exclude: Option<String>,
output: Option<PathBuf>,
_perf: bool,
fuzzy: bool,
case_sensitive: bool,
) -> Result<()> {
eprintln!("🔍 Searching for names similar to '{query}'...");
let names = collect_names(&project_path, &include, &exclude, scope).await?;
eprintln!("✅ Found {} names to analyze", names.len());
let matches = find_similar_names(&query, names, threshold, phonetic, fuzzy, case_sensitive)?;
let mut top_matches = matches;
top_matches.sort_by(|a, b| {
b.similarity_score
.partial_cmp(&a.similarity_score)
.expect("internal error")
});
top_matches.truncate(top_k);
let result = NameSimilarityResult {
query: query.clone(),
total_candidates: top_matches.len(),
matches: top_matches,
search_scope: format!("{scope:?}"),
};
let content = format_output(result, format)?;
if let Some(output_path) = output {
tokio::fs::write(&output_path, &content).await?;
eprintln!("✅ Results written to: {}", output_path.display());
} else {
println!("{content}");
}
Ok(())
}
include!("name_similarity_file_collection.rs");
include!("name_similarity_scoring.rs");
include!("name_similarity_formatting.rs");
include!("name_similarity_tests.rs");