#![allow(dead_code)]
use regex::Regex;
use std::sync::LazyLock;
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionMetrics {
pub function_count: usize,
pub max_function_length: usize,
pub avg_function_length: f64,
pub functions_over_threshold: usize,
}
impl Default for FunctionMetrics {
fn default() -> Self {
Self {
function_count: 0,
max_function_length: 0,
avg_function_length: 0.0,
functions_over_threshold: 0,
}
}
}
const LONG_FUNCTION_THRESHOLD: usize = 100;
#[derive(Debug, Clone)]
struct FunctionSpan {
start_line: usize,
end_line: usize,
}
impl FunctionSpan {
fn length(&self) -> usize {
self.end_line.saturating_sub(self.start_line) + 1
}
}
static RUST_FN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"^\s*(pub(\([^)]+\))?\s+)?((async|unsafe|const|extern\s+"[^"]*")\s+)*fn\s+(?:r#)?(?:_|[\p{XID_Start}])\p{XID_Continue}*"#)
.expect("Static regex must compile")
});
static PYTHON_DEF: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^\s*(async\s+)?def\s+\w+").expect("Static regex must compile"));
static JS_FUNCTION: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^\s*(export\s+)?(async\s+)?function\s+\w+").expect("Static regex must compile")
});
static JS_ARROW: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^\s*(export\s+)?(const|let|var)\s+\w+\s*=\s*(async\s+)?\([^)]*\)\s*=>")
.expect("Static regex must compile")
});
static JS_METHOD: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^\s*(async\s+)?\w+\s*\([^)]*\)\s*\{").expect("Static regex must compile")
});
static GO_FUNC: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^\s*func\s+\w+").expect("Static regex must compile"));
pub fn analyze_functions(content: &str, language: &str) -> FunctionMetrics {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return FunctionMetrics::default();
}
let spans = match lang.as_str() {
"rust" | "rs" => detect_brace_functions(&lines, &RUST_FN),
"python" | "py" => detect_indented_functions(&lines, &PYTHON_DEF),
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => detect_js_functions(&lines),
"go" => detect_brace_functions(&lines, &GO_FUNC),
_ => Vec::new(),
};
compute_metrics(&spans)
}
fn detect_brace_functions(lines: &[&str], pattern: &Regex) -> Vec<FunctionSpan> {
let mut spans = Vec::new();
let mut i = 0;
while i < lines.len() {
if pattern.is_match(lines[i]) {
let start = i;
if let Some(end) = find_brace_end(lines, i) {
spans.push(FunctionSpan {
start_line: start,
end_line: end,
});
i = end + 1;
} else {
i += 1;
}
} else {
i += 1;
}
}
spans
}
fn find_brace_end(lines: &[&str], start_line: usize) -> Option<usize> {
let mut brace_count: usize = 0;
let mut found_open = false;
for (i, line) in lines.iter().enumerate().skip(start_line) {
for ch in line.chars() {
if ch == '{' {
brace_count += 1;
found_open = true;
} else if ch == '}' {
brace_count = brace_count.saturating_sub(1);
if found_open && brace_count == 0 {
return Some(i);
}
}
}
}
None
}
fn detect_indented_functions(lines: &[&str], pattern: &Regex) -> Vec<FunctionSpan> {
let mut spans = Vec::new();
let mut i = 0;
while i < lines.len() {
if pattern.is_match(lines[i]) {
let mut start = i;
let base_indent = get_indent(lines[i]);
{
let mut probe = start;
while probe > 0 {
let prev = lines[probe - 1].trim();
if prev.is_empty() {
probe -= 1;
continue;
}
let prev_indent = get_indent(lines[probe - 1]);
if prev_indent == base_indent && prev.starts_with('@') {
probe -= 1;
start = probe; } else {
break;
}
}
}
let end = find_indent_end(lines, i, base_indent);
spans.push(FunctionSpan {
start_line: start,
end_line: end,
});
i = end + 1;
} else {
i += 1;
}
}
spans
}
fn get_indent(line: &str) -> usize {
line.len() - line.trim_start().len()
}
fn find_indent_end(lines: &[&str], start_line: usize, base_indent: usize) -> usize {
let mut last_content_line = start_line;
for (i, line) in lines.iter().enumerate().skip(start_line + 1) {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let indent = get_indent(line);
if indent <= base_indent {
return last_content_line;
}
last_content_line = i;
}
last_content_line
}
fn detect_js_functions(lines: &[&str]) -> Vec<FunctionSpan> {
let mut spans = Vec::new();
let mut i = 0;
while i < lines.len() {
let line = lines[i];
if JS_FUNCTION.is_match(line) || JS_ARROW.is_match(line) || JS_METHOD.is_match(line) {
if is_likely_function_start(line) {
let start = i;
if let Some(end) = find_brace_end(lines, i) {
spans.push(FunctionSpan {
start_line: start,
end_line: end,
});
i = end + 1;
continue;
}
}
}
i += 1;
}
spans
}
fn is_likely_function_start(line: &str) -> bool {
let trimmed = line.trim();
!trimmed.starts_with("//")
&& !trimmed.starts_with("/*")
&& !trimmed.starts_with('*')
&& !trimmed.ends_with(',')
&& !trimmed.ends_with(';')
}
fn compute_metrics(spans: &[FunctionSpan]) -> FunctionMetrics {
if spans.is_empty() {
return FunctionMetrics::default();
}
let lengths: Vec<usize> = spans.iter().map(|s| s.length()).collect();
let total: usize = lengths.iter().sum();
let max = lengths.iter().copied().max().unwrap_or(0);
let over_threshold = lengths
.iter()
.filter(|&&l| l > LONG_FUNCTION_THRESHOLD)
.count();
FunctionMetrics {
function_count: spans.len(),
max_function_length: max,
avg_function_length: total as f64 / spans.len() as f64,
functions_over_threshold: over_threshold,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CyclomaticComplexity {
pub total_cc: usize,
pub max_cc: usize,
pub avg_cc: f64,
pub high_complexity_functions: Vec<HighComplexityFunction>,
pub function_count: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HighComplexityFunction {
pub name: String,
pub line: usize,
pub complexity: usize,
}
impl Default for CyclomaticComplexity {
fn default() -> Self {
Self {
total_cc: 0,
max_cc: 0,
avg_cc: 0.0,
high_complexity_functions: Vec::new(),
function_count: 0,
}
}
}
const HIGH_COMPLEXITY_THRESHOLD: usize = 10;
pub fn estimate_cyclomatic_complexity(content: &str, language: &str) -> CyclomaticComplexity {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return CyclomaticComplexity::default();
}
let spans = match lang.as_str() {
"rust" | "rs" => detect_brace_functions(&lines, &RUST_FN),
"python" | "py" => detect_indented_functions(&lines, &PYTHON_DEF),
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => detect_js_functions(&lines),
"go" => detect_brace_functions(&lines, &GO_FUNC),
_ => Vec::new(),
};
if spans.is_empty() {
return CyclomaticComplexity::default();
}
let mut complexities: Vec<(String, usize, usize)> = Vec::new();
for span in &spans {
let func_name = extract_function_name(&lines, span.start_line, &lang);
let func_lines: Vec<&str> = lines[span.start_line..=span.end_line].to_vec();
let cc = calculate_cyclomatic_complexity(&func_lines, &lang);
complexities.push((func_name, span.start_line + 1, cc)); }
let total_cc: usize = complexities.iter().map(|(_, _, cc)| cc).sum();
let max_cc = complexities.iter().map(|(_, _, cc)| *cc).max().unwrap_or(0);
let function_count = complexities.len();
let avg_cc = if function_count > 0 {
total_cc as f64 / function_count as f64
} else {
0.0
};
let high_complexity_functions: Vec<HighComplexityFunction> = complexities
.iter()
.filter(|(_, _, cc)| *cc > HIGH_COMPLEXITY_THRESHOLD)
.map(|(name, line, cc)| HighComplexityFunction {
name: name.clone(),
line: *line,
complexity: *cc,
})
.collect();
CyclomaticComplexity {
total_cc,
max_cc,
avg_cc,
high_complexity_functions,
function_count,
}
}
fn extract_function_name(lines: &[&str], start_line: usize, lang: &str) -> String {
let line = lines.get(start_line).unwrap_or(&"");
match lang {
"rust" | "rs" => {
if let Some(pos) = line.find("fn ") {
let after_fn = &line[pos + 3..];
return extract_identifier(after_fn);
}
}
"python" | "py" => {
if let Some(pos) = line.find("def ") {
let after_def = &line[pos + 4..];
return extract_identifier(after_def);
}
}
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => {
if let Some(pos) = line.find("function ") {
let after_func = &line[pos + 9..];
return extract_identifier(after_func);
}
if let Some(pos) = line.find("const ") {
let after_const = &line[pos + 6..];
return extract_identifier(after_const);
}
if let Some(pos) = line.find("let ") {
let after_let = &line[pos + 4..];
return extract_identifier(after_let);
}
let trimmed = line.trim();
if let Some(paren_pos) = trimmed.find('(') {
let before_paren = &trimmed[..paren_pos];
let words: Vec<&str> = before_paren.split_whitespace().collect();
if let Some(last) = words.last() {
return (*last).to_string();
}
}
}
"go" => {
if let Some(pos) = line.find("func ") {
let after_func = &line[pos + 5..];
return extract_identifier(after_func);
}
}
_ => {}
}
"unknown".to_string()
}
fn extract_identifier(s: &str) -> String {
let mut name = String::new();
let mut started = false;
for ch in s.chars() {
if !started {
if ch.is_alphabetic() || ch == '_' {
started = true;
name.push(ch);
}
} else if ch.is_alphanumeric() || ch == '_' {
name.push(ch);
} else {
break;
}
}
if name.is_empty() {
"unknown".to_string()
} else {
name
}
}
fn calculate_cyclomatic_complexity(lines: &[&str], lang: &str) -> usize {
let mut complexity = 1;
for line in lines {
let trimmed = line.trim();
if is_comment_line(trimmed, lang) {
continue;
}
complexity += count_decision_points(trimmed, lang);
}
complexity
}
fn is_comment_line(trimmed: &str, lang: &str) -> bool {
match lang {
"python" | "py" => trimmed.starts_with('#'),
_ => {
trimmed.starts_with("//")
|| trimmed.starts_with("/*")
|| trimmed.starts_with('*')
|| trimmed.starts_with("*/")
}
}
}
fn count_decision_points(line: &str, lang: &str) -> usize {
let mut count = 0;
match lang {
"rust" | "rs" => {
let else_if_count = count_keyword(line, "else if ");
count += else_if_count;
count += count_standalone_if(line, else_if_count);
count += count_keyword(line, "match ");
count += count_keyword(line, "for ");
count += count_keyword(line, "while ");
count += count_keyword(line, "loop ");
count += line.matches("&&").count();
count += line.matches("||").count();
count += count_rust_try_op(line);
count += line.matches("=>").count(); }
"python" | "py" => {
count += count_keyword(line, "if ");
count += count_keyword(line, "elif ");
count += count_keyword(line, "for ");
count += count_keyword(line, "while ");
count += count_keyword(line, "except ");
count += count_keyword(line, "except:");
count += line.matches(" and ").count();
count += line.matches(" or ").count();
}
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => {
let else_if_count = count_keyword(line, "else if ") + count_keyword(line, "else if(");
count += else_if_count;
count += count_standalone_if_js(line, else_if_count);
count += count_keyword(line, "switch ");
count += count_keyword(line, "switch(");
count += count_keyword(line, "for ");
count += count_keyword(line, "for(");
count += count_keyword(line, "while ");
count += count_keyword(line, "while(");
count += count_keyword(line, "catch ");
count += count_keyword(line, "catch(");
count += count_keyword(line, "case ");
count += line.matches("&&").count();
count += line.matches("||").count();
count += count_ternary_op(line);
}
"go" => {
let else_if_count = count_keyword(line, "else if ");
count += else_if_count;
count += count_standalone_if(line, else_if_count);
count += count_keyword(line, "switch ");
count += count_keyword(line, "select ");
count += count_keyword(line, "for ");
count += count_keyword(line, "case ");
count += line.matches("&&").count();
count += line.matches("||").count();
}
_ => {}
}
count
}
fn count_standalone_if(line: &str, else_if_count: usize) -> usize {
let total_if = count_keyword(line, "if ");
total_if.saturating_sub(else_if_count)
}
fn count_standalone_if_js(line: &str, else_if_count: usize) -> usize {
let total_if = count_keyword(line, "if ") + count_keyword(line, "if(");
total_if.saturating_sub(else_if_count)
}
fn count_keyword(line: &str, keyword: &str) -> usize {
let mut count = 0;
let mut pos = 0;
while let Some(idx) = line[pos..].find(keyword) {
let abs_pos = pos + idx;
let before_ok = abs_pos == 0
|| !line[..abs_pos]
.chars()
.last()
.map(|c| c.is_alphanumeric() || c == '_')
.unwrap_or(false);
if before_ok {
count += 1;
}
pos = abs_pos + keyword.len();
}
count
}
fn count_rust_try_op(line: &str) -> usize {
let mut count = 0;
let chars: Vec<char> = line.chars().collect();
for (i, &ch) in chars.iter().enumerate() {
if ch == '?' {
let prev = if i > 0 { chars.get(i - 1) } else { None };
let next = chars.get(i + 1);
if prev == Some(&':') || prev == Some(&'#') {
continue;
}
let is_try = next.is_none()
|| matches!(
next,
Some(';') | Some(')') | Some('}') | Some(',') | Some(' ')
);
let is_optional_chain = next == Some(&'.');
if is_try && !is_optional_chain {
count += 1;
}
}
}
count
}
fn count_ternary_op(line: &str) -> usize {
let mut count = 0;
let chars: Vec<char> = line.chars().collect();
for (i, &ch) in chars.iter().enumerate() {
if ch == '?' {
let next = chars.get(i + 1);
let is_optional_chain = next == Some(&'.');
let at_end = next.is_none() || matches!(next, Some(';') | Some(')'));
let has_colon = line[i..].contains(':');
if !is_optional_chain && !at_end && has_colon {
count += 1;
}
}
}
count
}
#[derive(Debug, Clone, PartialEq)]
pub struct CognitiveComplexity {
pub total: usize,
pub max: usize,
pub avg: f64,
pub function_count: usize,
pub high_complexity_functions: Vec<HighCognitiveFunction>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HighCognitiveFunction {
pub name: String,
pub line: usize,
pub complexity: usize,
}
impl Default for CognitiveComplexity {
fn default() -> Self {
Self {
total: 0,
max: 0,
avg: 0.0,
function_count: 0,
high_complexity_functions: Vec::new(),
}
}
}
const HIGH_COGNITIVE_THRESHOLD: usize = 15;
pub fn estimate_cognitive_complexity(content: &str, language: &str) -> CognitiveComplexity {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return CognitiveComplexity::default();
}
let spans = match lang.as_str() {
"rust" | "rs" => detect_brace_functions(&lines, &RUST_FN),
"python" | "py" => detect_indented_functions(&lines, &PYTHON_DEF),
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => detect_js_functions(&lines),
"go" => detect_brace_functions(&lines, &GO_FUNC),
"c" | "c++" | "cpp" | "java" | "c#" | "csharp" => detect_c_style_functions(&lines),
_ => Vec::new(),
};
if spans.is_empty() {
return CognitiveComplexity::default();
}
let mut complexities: Vec<(String, usize, usize)> = Vec::new();
for span in &spans {
let func_name = extract_function_name(&lines, span.start_line, &lang);
let func_lines: Vec<&str> = lines[span.start_line..=span.end_line].to_vec();
let cc = calculate_cognitive_complexity(&func_lines, &lang);
complexities.push((func_name, span.start_line + 1, cc)); }
let total: usize = complexities.iter().map(|(_, _, cc)| cc).sum();
let max = complexities.iter().map(|(_, _, cc)| *cc).max().unwrap_or(0);
let function_count = complexities.len();
let avg = if function_count > 0 {
total as f64 / function_count as f64
} else {
0.0
};
let high_complexity_functions: Vec<HighCognitiveFunction> = complexities
.iter()
.filter(|(_, _, cc)| *cc > HIGH_COGNITIVE_THRESHOLD)
.map(|(name, line, cc)| HighCognitiveFunction {
name: name.clone(),
line: *line,
complexity: *cc,
})
.collect();
CognitiveComplexity {
total,
max,
avg,
function_count,
high_complexity_functions,
}
}
fn detect_c_style_functions(lines: &[&str]) -> Vec<FunctionSpan> {
let mut spans = Vec::new();
let mut i = 0;
while i < lines.len() {
let line = lines[i];
let trimmed = line.trim();
let looks_like_fn = trimmed.ends_with(") {")
|| (trimmed.ends_with(')')
&& i + 1 < lines.len()
&& lines[i + 1].trim().starts_with('{'));
let is_control = trimmed.starts_with("if ")
|| trimmed.starts_with("if(")
|| trimmed.starts_with("while ")
|| trimmed.starts_with("while(")
|| trimmed.starts_with("for ")
|| trimmed.starts_with("for(")
|| trimmed.starts_with("switch ")
|| trimmed.starts_with("switch(")
|| trimmed.starts_with("catch ")
|| trimmed.starts_with("catch(");
if looks_like_fn && !is_control {
let start = i;
if let Some(end) = find_brace_end(lines, i) {
spans.push(FunctionSpan {
start_line: start,
end_line: end,
});
i = end + 1;
} else {
i += 1;
}
} else {
i += 1;
}
}
spans
}
fn calculate_cognitive_complexity(lines: &[&str], lang: &str) -> usize {
let mut complexity = 0usize;
let mut nesting_depth = 0usize;
let mut in_logical_sequence = false;
for line in lines {
let trimmed = line.trim();
if is_comment_line(trimmed, lang) {
continue;
}
let opens = count_structure_opens(trimmed, lang);
let closes = count_structure_closes(trimmed, lang);
let control_structures = count_control_structures(trimmed, lang);
for _ in 0..control_structures {
complexity += 1 + nesting_depth;
}
let (new_in_sequence, seq_complexity) =
count_logical_sequences(trimmed, in_logical_sequence);
complexity += seq_complexity;
in_logical_sequence = new_in_sequence;
if lang == "rust" || lang == "rs" {
complexity += count_labeled_jumps(trimmed);
}
nesting_depth = nesting_depth.saturating_add(opens);
nesting_depth = nesting_depth.saturating_sub(closes);
}
complexity
}
fn count_control_structures(line: &str, lang: &str) -> usize {
let mut count = 0;
match lang {
"rust" | "rs" => {
if line.contains("if ") && !line.contains("else if ") {
count += line.matches("if ").count();
}
if line.contains("else if ") {
count += line.matches("else if ").count();
}
count += count_keyword(line, "match ");
count += count_keyword(line, "for ");
count += count_keyword(line, "while ");
count += count_keyword(line, "loop ");
}
"python" | "py" => {
count += count_keyword(line, "if ");
count += count_keyword(line, "elif ");
count += count_keyword(line, "for ");
count += count_keyword(line, "while ");
count += count_keyword(line, "except ");
count += count_keyword(line, "except:");
}
"javascript" | "js" | "typescript" | "ts" | "jsx" | "tsx" => {
let else_if_count = count_keyword(line, "else if ") + count_keyword(line, "else if(");
count += else_if_count;
let total_if = count_keyword(line, "if ") + count_keyword(line, "if(");
count += total_if.saturating_sub(else_if_count);
count += count_keyword(line, "switch ");
count += count_keyword(line, "switch(");
count += count_keyword(line, "for ");
count += count_keyword(line, "for(");
count += count_keyword(line, "while ");
count += count_keyword(line, "while(");
count += count_keyword(line, "catch ");
count += count_keyword(line, "catch(");
}
"go" => {
let else_if_count = count_keyword(line, "else if ");
count += else_if_count;
let total_if = count_keyword(line, "if ");
count += total_if.saturating_sub(else_if_count);
count += count_keyword(line, "switch ");
count += count_keyword(line, "select ");
count += count_keyword(line, "for ");
}
"c" | "c++" | "cpp" | "java" | "c#" | "csharp" => {
let else_if_count = count_keyword(line, "else if ") + count_keyword(line, "else if(");
count += else_if_count;
let total_if = count_keyword(line, "if ") + count_keyword(line, "if(");
count += total_if.saturating_sub(else_if_count);
count += count_keyword(line, "switch ");
count += count_keyword(line, "switch(");
count += count_keyword(line, "for ");
count += count_keyword(line, "for(");
count += count_keyword(line, "while ");
count += count_keyword(line, "while(");
count += count_keyword(line, "catch ");
count += count_keyword(line, "catch(");
}
_ => {}
}
count
}
fn count_structure_opens(line: &str, lang: &str) -> usize {
match lang {
"python" | "py" => {
let mut count = 0;
if line.contains("if ") || line.contains("elif ") {
count += 1;
}
if line.contains("for ") || line.contains("while ") {
count += 1;
}
if line.contains("try:") || line.contains("except ") || line.contains("except:") {
count += 1;
}
if line.contains("with ") {
count += 1;
}
count
}
_ => line.chars().filter(|&c| c == '{').count(),
}
}
fn count_structure_closes(line: &str, lang: &str) -> usize {
match lang {
"python" | "py" => {
0
}
_ => line.chars().filter(|&c| c == '}').count(),
}
}
fn count_logical_sequences(line: &str, was_in_sequence: bool) -> (bool, usize) {
let has_and = line.contains("&&") || line.contains(" and ");
let has_or = line.contains("||") || line.contains(" or ");
if has_and || has_or {
let cost = if was_in_sequence { 0 } else { 1 };
(true, cost)
} else {
(false, 0)
}
}
fn count_labeled_jumps(line: &str) -> usize {
let mut count = 0;
if line.contains("break '") {
count += 1;
}
if line.contains("continue '") {
count += 1;
}
count
}
#[derive(Debug, Clone, PartialEq)]
pub struct NestingAnalysis {
pub max_depth: usize,
pub avg_depth: f64,
pub max_depth_lines: Vec<usize>,
}
impl Default for NestingAnalysis {
fn default() -> Self {
Self {
max_depth: 0,
avg_depth: 0.0,
max_depth_lines: Vec::new(),
}
}
}
pub fn analyze_nesting_depth(content: &str, language: &str) -> NestingAnalysis {
let lang = language.to_lowercase();
let lines: Vec<&str> = content.lines().collect();
if lines.is_empty() {
return NestingAnalysis::default();
}
match lang.as_str() {
"python" | "py" => analyze_indentation_depth(&lines),
_ => analyze_brace_depth(&lines, &lang),
}
}
fn analyze_brace_depth(lines: &[&str], lang: &str) -> NestingAnalysis {
let mut current_depth = 0usize;
let mut max_depth = 0usize;
let mut max_depth_lines: Vec<usize> = Vec::new();
let mut total_depth = 0usize;
let mut counted_lines = 0usize;
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if is_comment_line(trimmed, lang) {
continue;
}
let opens = line.chars().filter(|&c| c == '{').count();
let closes = line.chars().filter(|&c| c == '}').count();
let line_max_depth = current_depth + opens;
if line_max_depth > max_depth {
max_depth = line_max_depth;
max_depth_lines.clear();
max_depth_lines.push(i + 1); } else if line_max_depth == max_depth && !max_depth_lines.contains(&(i + 1)) {
max_depth_lines.push(i + 1);
}
current_depth = current_depth.saturating_add(opens);
current_depth = current_depth.saturating_sub(closes);
total_depth += current_depth;
counted_lines += 1;
}
let avg_depth = if counted_lines > 0 {
total_depth as f64 / counted_lines as f64
} else {
0.0
};
NestingAnalysis {
max_depth,
avg_depth,
max_depth_lines,
}
}
fn analyze_indentation_depth(lines: &[&str]) -> NestingAnalysis {
let mut max_depth = 0usize;
let mut max_depth_lines: Vec<usize> = Vec::new();
let mut total_depth = 0usize;
let mut counted_lines = 0usize;
let indent_unit = detect_indent_unit(lines);
for (i, line) in lines.iter().enumerate() {
if line.trim().is_empty() || line.trim().starts_with('#') {
continue;
}
let indent = get_indent(line);
let depth = indent.checked_div(indent_unit).unwrap_or(0);
if depth > max_depth {
max_depth = depth;
max_depth_lines.clear();
max_depth_lines.push(i + 1);
} else if depth == max_depth && !max_depth_lines.contains(&(i + 1)) {
max_depth_lines.push(i + 1);
}
total_depth += depth;
counted_lines += 1;
}
let avg_depth = if counted_lines > 0 {
total_depth as f64 / counted_lines as f64
} else {
0.0
};
NestingAnalysis {
max_depth,
avg_depth,
max_depth_lines,
}
}
fn detect_indent_unit(lines: &[&str]) -> usize {
for line in lines {
if line.starts_with('\t') {
return 1;
}
let indent = get_indent(line);
if indent > 0 && indent <= 8 {
return indent;
}
}
4 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rust_simple_function() {
let code = r#"
fn main() {
println!("Hello");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
assert_eq!(metrics.max_function_length, 3);
}
#[test]
fn rust_multiple_functions() {
let code = r#"
fn main() {
helper();
}
fn helper() {
// do something
}
pub fn public_helper() {
// public
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 3);
}
#[test]
fn rust_async_function() {
let code = r#"
async fn fetch_data() {
// async work
}
pub async fn public_async() {
// public async
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 2);
}
#[test]
fn rust_nested_braces() {
let code = r#"
fn complex() {
if true {
for i in 0..10 {
println!("{}", i);
}
}
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
assert_eq!(metrics.max_function_length, 7);
}
#[test]
fn rust_language_alias() {
let code = "fn test() {}";
let metrics = analyze_functions(code, "rs");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_pub_in_path_function() {
let code = r#"
pub(in crate::foo) fn bar() {
println!("hello");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_extern_c_function() {
let code = r#"
extern "C" fn callback() {
println!("called from C");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_pub_crate_unsafe_async_function() {
let code = r#"
pub(crate) unsafe async fn baz() {
println!("unsafe async");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_raw_identifier_function() {
let code = r#"
pub(crate) unsafe fn r#match() {
println!("raw ident");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_pub_super_const_function() {
let code = r#"
pub(super) const fn helper() -> u32 {
42
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_leading_underscore_function_name() {
let code = "fn _private_helper() {}";
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn rust_unicode_function_name() {
let code = r#"
fn café() {
println!("unicode");
}
fn ä½ å¥½() {
println!("chinese");
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 2);
}
#[test]
fn python_simple_function() {
let code = r#"
def main():
print("Hello")
"#;
let metrics = analyze_functions(code, "python");
assert_eq!(metrics.function_count, 1);
assert_eq!(metrics.max_function_length, 2);
}
#[test]
fn python_multiple_functions() {
let code = r#"
def main():
helper()
def helper():
pass
def another():
return 42
"#;
let metrics = analyze_functions(code, "python");
assert_eq!(metrics.function_count, 3);
}
#[test]
fn python_async_function() {
let code = r#"
async def fetch():
await something()
"#;
let metrics = analyze_functions(code, "python");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn python_nested_blocks() {
let code = r#"
def complex():
if True:
for i in range(10):
print(i)
return None
"#;
let metrics = analyze_functions(code, "python");
assert_eq!(metrics.function_count, 1);
assert_eq!(metrics.max_function_length, 5);
}
#[test]
fn python_function_with_comments() {
let code = r#"
def main():
# This is a comment
pass
# Another comment
"#;
let metrics = analyze_functions(code, "python");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn python_language_alias() {
let code = "def test():\n pass";
let metrics = analyze_functions(code, "py");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_function_declaration() {
let code = r#"
function main() {
console.log("Hello");
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_async_function() {
let code = r#"
async function fetchData() {
await fetch();
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_arrow_function() {
let code = r#"
const add = (a, b) => {
return a + b;
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_async_arrow_function() {
let code = r#"
const fetchData = async () => {
await fetch();
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_export_function() {
let code = r#"
export function helper() {
return 42;
}
export const util = () => {
return true;
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 2);
}
#[test]
fn js_method_syntax() {
let code = r#"
handleClick() {
this.setState({});
}
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn js_language_aliases() {
let code = "function test() {}";
assert_eq!(analyze_functions(code, "js").function_count, 1);
assert_eq!(analyze_functions(code, "typescript").function_count, 1);
assert_eq!(analyze_functions(code, "ts").function_count, 1);
assert_eq!(analyze_functions(code, "jsx").function_count, 1);
assert_eq!(analyze_functions(code, "tsx").function_count, 1);
}
#[test]
fn go_simple_function() {
let code = r#"
func main() {
fmt.Println("Hello")
}
"#;
let metrics = analyze_functions(code, "go");
assert_eq!(metrics.function_count, 1);
}
#[test]
fn go_multiple_functions() {
let code = r#"
func main() {
helper()
}
func helper() {
// do something
}
"#;
let metrics = analyze_functions(code, "go");
assert_eq!(metrics.function_count, 2);
}
#[test]
fn go_nested_braces() {
let code = r#"
func complex() {
if true {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
}
"#;
let metrics = analyze_functions(code, "go");
assert_eq!(metrics.function_count, 1);
assert_eq!(metrics.max_function_length, 7);
}
#[test]
fn empty_content() {
let metrics = analyze_functions("", "rust");
assert_eq!(metrics.function_count, 0);
assert_eq!(metrics.max_function_length, 0);
assert_eq!(metrics.avg_function_length, 0.0);
assert_eq!(metrics.functions_over_threshold, 0);
}
#[test]
fn no_functions() {
let code = r#"
// Just a comment
const x = 5;
"#;
let metrics = analyze_functions(code, "javascript");
assert_eq!(metrics.function_count, 0);
}
#[test]
fn unknown_language() {
let code = "fn main() {}";
let metrics = analyze_functions(code, "cobol");
assert_eq!(metrics.function_count, 0);
}
#[test]
fn case_insensitive_language() {
let code = "fn main() {}";
assert_eq!(analyze_functions(code, "RUST").function_count, 1);
assert_eq!(analyze_functions(code, "Rust").function_count, 1);
assert_eq!(analyze_functions(code, "RuSt").function_count, 1);
}
#[test]
fn avg_function_length_calculation() {
let code = r#"
fn short() {
x
}
fn longer() {
a
b
c
}
"#;
let metrics = analyze_functions(code, "rust");
assert_eq!(metrics.function_count, 2);
assert!((metrics.avg_function_length - 4.0).abs() < 0.01);
}
#[test]
fn functions_over_threshold() {
let mut code = String::from("fn very_long() {\n");
for i in 0..105 {
code.push_str(&format!(" line{};\n", i));
}
code.push_str("}\n");
let metrics = analyze_functions(&code, "rust");
assert_eq!(metrics.function_count, 1);
assert!(metrics.max_function_length > 100);
assert_eq!(metrics.functions_over_threshold, 1);
}
#[test]
fn mixed_function_lengths() {
let mut code = String::new();
code.push_str("fn short() {\n x\n}\n\n");
code.push_str("fn medium() {\n");
for _ in 0..48 {
code.push_str(" line;\n");
}
code.push_str("}\n\n");
code.push_str("fn long() {\n");
for _ in 0..148 {
code.push_str(" line;\n");
}
code.push_str("}\n");
let metrics = analyze_functions(&code, "rust");
assert_eq!(metrics.function_count, 3);
assert_eq!(metrics.functions_over_threshold, 1); assert_eq!(metrics.max_function_length, 150);
}
#[test]
fn cc_empty_content() {
let result = estimate_cyclomatic_complexity("", "rust");
assert_eq!(result.function_count, 0);
assert_eq!(result.total_cc, 0);
assert_eq!(result.max_cc, 0);
assert_eq!(result.avg_cc, 0.0);
}
#[test]
fn cc_unsupported_language() {
let result = estimate_cyclomatic_complexity("some code", "unknown_lang");
assert_eq!(result.function_count, 0);
assert_eq!(result.total_cc, 0);
}
#[test]
fn cc_no_functions() {
let rust_code = r#"
// Just comments
const X: i32 = 42;
"#;
let result = estimate_cyclomatic_complexity(rust_code, "rust");
assert_eq!(result.function_count, 0);
}
#[test]
fn cc_rust_simple_function() {
let code = r#"
fn hello() {
println!("Hello, world!");
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 1); }
#[test]
fn cc_rust_if_statement() {
let code = r#"
fn check(x: i32) {
if x > 0 {
println!("positive");
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2); }
#[test]
fn cc_rust_if_else_if() {
let code = r#"
fn check(x: i32) {
if x > 0 {
println!("positive");
} else if x < 0 {
println!("negative");
} else {
println!("zero");
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_rust_match_statement() {
let code = r#"
fn classify(x: i32) -> &'static str {
match x {
0 => "zero",
1..=10 => "small",
_ => "large",
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(result.total_cc >= 4);
}
#[test]
fn cc_rust_loops() {
let code = r#"
fn loops() {
for i in 0..10 {
println!("{}", i);
}
while true {
break;
}
loop {
break;
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_rust_logical_operators() {
let code = r#"
fn check(a: bool, b: bool, c: bool) {
if a && b || c {
println!("complex");
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_rust_try_operator() {
let code = r#"
fn fallible() -> Result<(), Error> {
let x = something()?;
let y = another()?;
Ok(())
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_rust_multiple_functions() {
let code = r#"
fn simple() {
println!("simple");
}
fn complex(x: i32) -> i32 {
if x > 0 {
if x > 10 {
x * 2
} else {
x
}
} else {
0
}
}
pub fn another() {
for i in 0..5 {
println!("{}", i);
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 3);
assert!(result.total_cc >= 6);
assert!(result.max_cc >= 3);
}
#[test]
fn cc_rust_pub_async_fn() {
let code = r#"
pub async fn fetch_data() {
if let Some(data) = get_data().await {
println!("{:?}", data);
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2);
}
#[test]
fn cc_python_simple_function() {
let code = r#"
def hello():
print("Hello")
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 1);
}
#[test]
fn cc_python_if_elif() {
let code = r#"
def check(x):
if x > 0:
print("positive")
elif x < 0:
print("negative")
else:
print("zero")
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_python_loops() {
let code = r#"
def process(items):
for item in items:
print(item)
while True:
break
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_python_logical_operators() {
let code = r#"
def check(a, b, c):
if a and b or c:
print("complex")
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_python_exception_handling() {
let code = r#"
def risky():
try:
something()
except ValueError:
handle()
except TypeError:
other()
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_python_async_def() {
let code = r#"
async def fetch():
if data:
return data
"#;
let result = estimate_cyclomatic_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2);
}
#[test]
fn cc_js_simple_function() {
let code = r#"
function hello() {
console.log("Hello");
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 1);
}
#[test]
fn cc_js_if_else_if() {
let code = r#"
function check(x) {
if (x > 0) {
console.log("positive");
} else if (x < 0) {
console.log("negative");
} else {
console.log("zero");
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_js_switch_case() {
let code = r#"
function classify(x) {
switch (x) {
case 0:
return "zero";
case 1:
return "one";
default:
return "other";
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_js_ternary_operator() {
let code = r#"
function max(a, b) {
return a > b ? a : b;
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2);
}
#[test]
fn cc_js_logical_operators() {
let code = r#"
function check(a, b) {
if (a && b || !a) {
return true;
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_js_try_catch() {
let code = r#"
function risky() {
try {
something();
} catch (e) {
console.error(e);
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2);
}
#[test]
fn cc_typescript_same_as_js() {
let code = r#"
function greet(name: string): void {
if (name) {
console.log(`Hello, ${name}`);
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "typescript");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 2);
}
#[test]
fn cc_go_simple_function() {
let code = r#"
func hello() {
fmt.Println("Hello")
}
"#;
let result = estimate_cyclomatic_complexity(code, "go");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 1);
}
#[test]
fn cc_go_if_else() {
let code = r#"
func check(x int) {
if x > 0 {
fmt.Println("positive")
} else if x < 0 {
fmt.Println("negative")
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "go");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 3);
}
#[test]
fn cc_go_switch_case() {
let code = r#"
func classify(x int) string {
switch x {
case 0:
return "zero"
case 1:
return "one"
default:
return "other"
}
}
"#;
let result = estimate_cyclomatic_complexity(code, "go");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 4);
}
#[test]
fn cc_high_complexity_function() {
let code = r#"
fn very_complex(x: i32) -> i32 {
if x > 0 {
if x > 10 {
if x > 100 {
for i in 0..x {
if i % 2 == 0 && i > 5 || i < 3 {
while i > 0 {
match i {
0 => return 0,
1 => return 1,
_ => continue,
}
}
}
}
}
}
}
0
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(
result.max_cc > 10,
"Expected high complexity, got {}",
result.max_cc
);
assert!(!result.high_complexity_functions.is_empty());
assert_eq!(result.high_complexity_functions[0].name, "very_complex");
}
#[test]
fn cc_comments_ignored() {
let code = r#"
fn example() {
// if this was real, it would add complexity
// for loops are cool
// while true {}
println!("actual code");
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total_cc, 1); }
#[test]
fn cc_average_complexity() {
let code = r#"
fn a() { }
fn b() { if true { } }
fn c() { if true { } if true { } }
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 3);
assert!((result.avg_cc - 2.0).abs() < 0.5);
}
#[test]
fn cc_language_aliases() {
let rust_code = "fn test() { }";
assert_eq!(
estimate_cyclomatic_complexity(rust_code, "rust").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(rust_code, "rs").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(rust_code, "RUST").function_count,
1
);
let py_code = "def test():\n pass";
assert_eq!(
estimate_cyclomatic_complexity(py_code, "python").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(py_code, "py").function_count,
1
);
let js_code = "function test() { }";
assert_eq!(
estimate_cyclomatic_complexity(js_code, "javascript").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(js_code, "js").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(js_code, "typescript").function_count,
1
);
assert_eq!(
estimate_cyclomatic_complexity(js_code, "ts").function_count,
1
);
}
#[test]
fn cc_extracts_function_names() {
let code = r#"
fn my_function() {
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
if true { }
}
"#;
let result = estimate_cyclomatic_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(!result.high_complexity_functions.is_empty());
assert_eq!(result.high_complexity_functions[0].name, "my_function");
assert!(result.high_complexity_functions[0].line > 0);
}
#[test]
fn cognitive_empty_content() {
let result = estimate_cognitive_complexity("", "rust");
assert_eq!(result.function_count, 0);
assert_eq!(result.total, 0);
assert_eq!(result.max, 0);
assert_eq!(result.avg, 0.0);
}
#[test]
fn cognitive_unsupported_language() {
let result = estimate_cognitive_complexity("some code", "unknown_lang");
assert_eq!(result.function_count, 0);
}
#[test]
fn cognitive_rust_simple_function() {
let code = r#"
fn hello() {
println!("Hello, world!");
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert_eq!(result.total, 0); }
#[test]
fn cognitive_rust_nested_if() {
let code = r#"
fn complex(x: i32) -> i32 {
if x > 0 {
if x > 10 {
if x > 100 {
return x * 2;
}
}
}
0
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(
result.total >= 3,
"Expected cognitive >= 3, got {}",
result.total
);
}
#[test]
fn cognitive_rust_loops_with_nesting() {
let code = r#"
fn process() {
for i in 0..10 {
while i > 0 {
loop {
break;
}
}
}
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(
result.total >= 3,
"Expected cognitive >= 3, got {}",
result.total
);
}
#[test]
fn cognitive_rust_logical_sequence() {
let code = r#"
fn check(a: bool, b: bool, c: bool, d: bool) {
if a && b && c || d {
println!("complex");
}
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(result.total >= 2);
}
#[test]
fn cognitive_rust_labeled_break() {
let code = r#"
fn labeled() {
'outer: for i in 0..10 {
for j in 0..10 {
if j == 5 {
break 'outer;
}
}
}
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(
result.total >= 4,
"Expected cognitive >= 4, got {}",
result.total
);
}
#[test]
fn cognitive_python_nested() {
let code = r#"
def complex():
if True:
for i in range(10):
while True:
break
"#;
let result = estimate_cognitive_complexity(code, "python");
assert_eq!(result.function_count, 1);
assert!(result.total >= 3);
}
#[test]
fn cognitive_js_nested() {
let code = r#"
function complex() {
if (true) {
for (let i = 0; i < 10; i++) {
while (true) {
break;
}
}
}
}
"#;
let result = estimate_cognitive_complexity(code, "javascript");
assert_eq!(result.function_count, 1);
assert!(result.total >= 3);
}
#[test]
fn cognitive_high_complexity_detection() {
let code = r#"
fn very_complex(x: i32) -> i32 {
if x > 0 {
if x > 1 {
if x > 2 {
if x > 3 {
if x > 4 {
if x > 5 {
if x > 6 {
return x;
}
}
}
}
}
}
}
0
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 1);
assert!(
result.max > 10,
"Expected high cognitive, got {}",
result.max
);
}
#[test]
fn cognitive_multiple_functions() {
let code = r#"
fn simple() {
println!("simple");
}
fn moderate() {
if true {
for i in 0..5 {
println!("{}", i);
}
}
}
"#;
let result = estimate_cognitive_complexity(code, "rust");
assert_eq!(result.function_count, 2);
assert!(result.avg > 0.0);
}
#[test]
fn nesting_empty_content() {
let result = analyze_nesting_depth("", "rust");
assert_eq!(result.max_depth, 0);
assert_eq!(result.avg_depth, 0.0);
assert!(result.max_depth_lines.is_empty());
}
#[test]
fn nesting_rust_no_braces() {
let code = "let x = 5;";
let result = analyze_nesting_depth(code, "rust");
assert_eq!(result.max_depth, 0);
}
#[test]
fn nesting_rust_simple_function() {
let code = r#"
fn main() {
println!("Hello");
}
"#;
let result = analyze_nesting_depth(code, "rust");
assert_eq!(result.max_depth, 1);
}
#[test]
fn nesting_rust_nested_blocks() {
let code = r#"
fn main() {
if true {
for i in 0..10 {
println!("{}", i);
}
}
}
"#;
let result = analyze_nesting_depth(code, "rust");
assert!(
result.max_depth >= 3 && result.max_depth <= 4,
"Expected max_depth 3-4, got {}",
result.max_depth
);
}
#[test]
fn nesting_rust_deeply_nested() {
let code = r#"
fn deep() {
if true {
if true {
if true {
if true {
if true {
println!("deep");
}
}
}
}
}
}
"#;
let result = analyze_nesting_depth(code, "rust");
assert_eq!(result.max_depth, 6);
assert!(!result.max_depth_lines.is_empty());
}
#[test]
fn nesting_python_simple() {
let code = r#"
def main():
print("Hello")
"#;
let result = analyze_nesting_depth(code, "python");
assert_eq!(result.max_depth, 1);
}
#[test]
fn nesting_python_nested() {
let code = r#"
def main():
if True:
for i in range(10):
print(i)
"#;
let result = analyze_nesting_depth(code, "python");
assert_eq!(result.max_depth, 3);
}
#[test]
fn nesting_js_nested() {
let code = r#"
function main() {
if (true) {
for (let i = 0; i < 10; i++) {
console.log(i);
}
}
}
"#;
let result = analyze_nesting_depth(code, "javascript");
assert_eq!(result.max_depth, 3);
}
#[test]
fn nesting_go_nested() {
let code = r#"
func main() {
if true {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
}
"#;
let result = analyze_nesting_depth(code, "go");
assert_eq!(result.max_depth, 3);
}
#[test]
fn nesting_average_calculation() {
let code = r#"
fn main() {
let a = 1;
if true {
let b = 2;
}
}
"#;
let result = analyze_nesting_depth(code, "rust");
assert!(result.avg_depth > 0.0);
}
#[test]
fn nesting_max_depth_lines_tracked() {
let code = r#"
fn main() {
if true {
for i in 0..10 {
println!("{}", i);
}
}
}
"#;
let result = analyze_nesting_depth(code, "rust");
assert!(
result.max_depth >= 3,
"Expected max_depth >= 3, got {}",
result.max_depth
);
assert!(!result.max_depth_lines.is_empty());
}
}