use super::pattern_matchers::{matches_debug_pattern, matches_output_io_pattern};
use crate::analyzers::rust_constructor_detector::{
analyze_function_body, extract_return_type, ConstructorReturnType,
};
use crate::analyzers::rust_enum_converter_detector::is_enum_converter;
use crate::core::FunctionMetrics;
use crate::priority::call_graph::{CallGraph, FunctionId};
pub(crate) fn is_debug_function(func: &FunctionMetrics) -> bool {
let name_matches = matches_debug_pattern(&func.name);
let has_debug_characteristics = has_diagnostic_characteristics(func);
if name_matches {
return func.cognitive <= 10;
}
has_debug_characteristics
}
pub(crate) fn has_diagnostic_characteristics(func: &FunctionMetrics) -> bool {
let is_very_simple = func.cognitive < 5 && func.length < 20;
let has_output_io_name = matches_output_io_pattern(&func.name);
is_very_simple && has_output_io_name
}
pub(crate) fn is_simple_constructor(func: &FunctionMetrics) -> bool {
let config = crate::config::get_constructor_detection_config();
let name_lower = func.name.to_lowercase();
let matches_constructor_name = config.patterns.iter().any(|pattern| {
name_lower == *pattern || name_lower.starts_with(pattern) || name_lower.ends_with(pattern)
});
let is_simple = func.cyclomatic <= config.max_cyclomatic
&& func.length < config.max_length
&& func.nesting <= config.max_nesting;
let is_initialization = func.cognitive <= config.max_cognitive;
matches_constructor_name && is_simple && is_initialization
}
pub(crate) fn is_constructor_enhanced(
func: &FunctionMetrics,
syn_func: Option<&syn::ItemFn>,
) -> bool {
let config = crate::config::get_constructor_detection_config();
if !config.ast_detection || syn_func.is_none() {
return is_simple_constructor(func);
}
let syn_func = syn_func.unwrap();
let return_type = extract_return_type(syn_func);
let body_pattern = analyze_function_body(syn_func);
let returns_self = matches!(
return_type,
Some(
ConstructorReturnType::OwnedSelf
| ConstructorReturnType::ResultSelf
| ConstructorReturnType::OptionSelf
)
);
if !returns_self {
return is_simple_constructor(func);
}
if !body_pattern.is_constructor_like() {
return false;
}
let is_simple_enough =
func.cyclomatic <= 5 && func.nesting <= 2 && func.length < 30 && !body_pattern.has_loop;
returns_self && is_simple_enough
}
pub(crate) fn is_enum_converter_enhanced(func: &FunctionMetrics, syn_func: &syn::ItemFn) -> bool {
is_enum_converter(func, syn_func)
}
pub(crate) fn is_pattern_matching_function(func: &FunctionMetrics, func_id: &FunctionId) -> bool {
let name_lower = func_id.name.to_lowercase();
let pattern_match_names = [
"detect",
"classify",
"identify",
"determine",
"resolve",
"match",
"parse_type",
"get_type",
"find_type",
];
let name_matches = pattern_match_names
.iter()
.any(|pattern| name_lower.contains(pattern));
if name_matches && func.cyclomatic <= 2 {
let ratio = if func.cyclomatic > 0 {
func.cognitive as f32 / func.cyclomatic as f32
} else {
func.cognitive as f32
};
return ratio > 5.0;
}
false
}
pub(crate) fn is_orchestrator(
func: &FunctionMetrics,
func_id: &FunctionId,
call_graph: &CallGraph,
) -> bool {
use super::pattern_matchers::is_orchestrator_by_name;
let callees = call_graph.get_callees(func_id);
let meaningful_callees: Vec<_> = callees
.iter()
.filter(|f| {
!matches!(
f.name.as_str(),
"format" | "write" | "print" | "println" | "clone" | "to_string" | "into" | "from"
) && !f.name.starts_with("std::")
&& !f.name.starts_with("core::")
&& !f.name.starts_with("alloc::")
})
.collect();
if !meaningful_callees.is_empty() && callees.len() > meaningful_callees.len() {
let functional_chain = callees.iter().all(|f| {
matches!(
f.name.as_str(),
"format" | "write" | "print" | "println" | "clone" | "to_string" | "into" | "from"
) || f.name.starts_with("std::")
|| f.name.starts_with("core::")
|| f.name.starts_with("alloc::")
|| f.name.contains("Pipeline")
|| f.name.contains("Stream")
|| f.name.contains("Iterator")
});
if functional_chain {
return false;
}
}
if meaningful_callees.len() == 1 {
return false;
}
if meaningful_callees.len() < 2 {
return false;
}
let delegation_ratio = calculate_delegation_ratio(func, &meaningful_callees);
let name_suggests_orchestration =
is_orchestrator_by_name(&func_id.name) && func.cyclomatic <= 5;
let is_simple_delegation = func.cyclomatic <= 5
&& delegation_ratio >= 0.2
&& delegates_to_tested_functions(func_id, call_graph, 0.8);
name_suggests_orchestration || is_simple_delegation
}
pub(crate) fn is_io_wrapper(func: &FunctionMetrics) -> bool {
if !contains_io_patterns(func) {
return false;
}
if func.length < 20 {
return true;
}
func.length <= 50 && is_io_orchestration(func)
}
pub(crate) fn calculate_delegation_ratio(
func: &FunctionMetrics,
meaningful_callees: &[&FunctionId],
) -> f64 {
if func.length == 0 {
return 0.0;
}
meaningful_callees.len() as f64 / func.length as f64
}
pub(crate) fn delegates_to_tested_functions(
func_id: &FunctionId,
call_graph: &CallGraph,
_threshold: f64,
) -> bool {
let callees = call_graph.get_callees(func_id);
if callees.is_empty() {
return false;
}
let meaningful_callees: Vec<_> = callees
.iter()
.filter(|f| {
!matches!(
f.name.as_str(),
"format" | "write" | "print" | "println" | "clone" | "to_string" | "into" | "from"
) && !f.name.starts_with("std::")
&& !f.name.starts_with("core::")
&& !f.name.starts_with("alloc::")
})
.collect();
meaningful_callees.len() >= 2 && call_graph.detect_delegation_pattern(func_id)
}
fn contains_io_patterns(func: &FunctionMetrics) -> bool {
let io_keywords = vec![
"read",
"write",
"file",
"socket",
"stream",
"buffer",
"stdin",
"stdout",
"stderr",
"bufreader",
"bufwriter",
"http",
"request",
"response",
"spawn",
"command",
"process",
"child",
"exec",
"timeout",
"async",
"await",
"tokio",
"future",
"print",
"input",
"output",
"display",
"json",
"serialize",
"deserialize",
"emit",
"render",
"save",
"load",
"export",
"import",
"log",
"trace",
"debug",
"info",
"warn",
"error",
"summary",
"report",
];
let name_lower = func.name.to_lowercase();
io_keywords
.iter()
.any(|keyword| name_lower.contains(keyword))
}
fn is_io_orchestration(func: &FunctionMetrics) -> bool {
let name_lower = func.name.to_lowercase();
let strong_io_patterns = [
"output_",
"write_",
"print_",
"format_",
"serialize_",
"save_",
"export_",
"display_",
"render_",
"emit_",
];
let has_strong_io_name = strong_io_patterns
.iter()
.any(|pattern| name_lower.starts_with(pattern));
has_strong_io_name && func.nesting <= 3
}