pub mod cross_project;
mod crud;
mod dead_code;
mod query;
mod related;
mod test_map;
use std::path::PathBuf;
use std::sync::LazyLock;
use regex::Regex;
use super::helpers::ChunkSummary;
use crate::parser::{ChunkType, Language};
#[derive(Debug, Clone, serde::Serialize)]
pub struct DeadFunction {
pub chunk: ChunkSummary,
pub confidence: DeadConfidence,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, clap::ValueEnum)]
pub enum DeadConfidence {
Low,
Medium,
High,
}
impl DeadConfidence {
pub fn as_str(&self) -> &'static str {
match self {
DeadConfidence::Low => "low",
DeadConfidence::Medium => "medium",
DeadConfidence::High => "high",
}
}
}
impl std::fmt::Display for DeadConfidence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
const FALLBACK_ENTRY_POINT_NAMES: &[&str] = &["main", "new"];
fn build_entry_point_names() -> Vec<&'static str> {
let mut names = crate::language::REGISTRY.all_entry_point_names();
let mut seen: std::collections::HashSet<&str> = names.iter().copied().collect();
for name in FALLBACK_ENTRY_POINT_NAMES {
if seen.insert(name) {
names.push(name);
}
}
names
}
#[derive(Debug, Clone)]
pub(crate) struct LightChunk {
pub id: String,
pub file: PathBuf,
pub language: Language,
pub chunk_type: ChunkType,
pub name: String,
pub signature: String,
pub line_start: u32,
pub line_end: u32,
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct CallStats {
pub total_calls: u64,
pub unique_callees: u64,
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct FunctionCallStats {
pub total_calls: u64,
pub unique_callers: u64,
pub unique_callees: u64,
}
static TRAIT_IMPL_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"impl\s+\w+\s+for\s+").expect("hardcoded regex"));
const TEST_NAME_PATTERNS: &[&str] = &["test_%", "Test%"];
const FALLBACK_TEST_CONTENT_MARKERS: &[&str] = &["#[test]", "@Test"];
const FALLBACK_TEST_PATH_PATTERNS: &[&str] = &[
"%/tests/%",
"%\\_test.%",
"%.test.%",
"%.spec.%",
"%_test.go",
"%_test.py",
];
fn build_test_content_markers() -> Vec<&'static str> {
let markers = crate::language::REGISTRY.all_test_markers();
if markers.is_empty() {
FALLBACK_TEST_CONTENT_MARKERS.to_vec()
} else {
markers
}
}
fn build_test_path_patterns() -> Vec<&'static str> {
let patterns = crate::language::REGISTRY.all_test_path_patterns();
if patterns.is_empty() {
FALLBACK_TEST_PATH_PATTERNS.to_vec()
} else {
patterns
}
}
const FALLBACK_TRAIT_METHOD_NAMES: &[&str] = &["new", "build", "builder"];
fn build_trait_method_names() -> Vec<&'static str> {
let mut names = crate::language::REGISTRY.all_trait_method_names();
let mut seen: std::collections::HashSet<&str> = names.iter().copied().collect();
for name in FALLBACK_TRAIT_METHOD_NAMES {
if seen.insert(name) {
names.push(name);
}
}
names
}
fn build_test_chunk_filter() -> String {
let mut clauses: Vec<String> = Vec::new();
for pat in TEST_NAME_PATTERNS {
clauses.push(format!("name LIKE '{pat}'"));
}
for marker in build_test_content_markers() {
clauses.push(format!("content LIKE '%{marker}%'"));
}
for pat in build_test_path_patterns() {
if pat.contains("\\_") {
clauses.push(format!("origin LIKE '{pat}' ESCAPE '\\'"));
} else {
clauses.push(format!("origin LIKE '{pat}'"));
}
}
clauses.join("\n OR ")
}
static TEST_CHUNKS_SQL: LazyLock<String> = LazyLock::new(|| {
let filter = build_test_chunk_filter();
let callable = ChunkType::callable_sql_list();
format!(
"SELECT id, origin, language, chunk_type, name, signature,
line_start, line_end, parent_id, parent_type_name
FROM chunks
WHERE chunk_type IN ({callable})
AND (
{filter}
)
ORDER BY origin, line_start"
)
});
static TEST_CHUNK_NAMES_SQL: LazyLock<String> = LazyLock::new(|| {
let filter = build_test_chunk_filter();
let callable = ChunkType::callable_sql_list();
format!(
"SELECT DISTINCT name
FROM chunks
WHERE chunk_type IN ({callable})
AND (
{filter}
)"
)
});