use super::super::types::{FunctionInfo, StructureInfo, StructureType, Visibility};
use super::LanguageAnalyzer;
use crate::utils::errors::Result;
pub struct RustAnalyzer;
impl RustAnalyzer {
pub fn new() -> Self {
Self
}
fn extract_function_name(&self, line: &str) -> Option<String> {
if let Some(start) = line.find("fn ") {
let after_fn = &line[start + 3..];
after_fn
.find('(')
.map(|end| after_fn[..end].trim().to_string())
} else {
None
}
}
fn count_complexity_keywords(&self, line: &str) -> usize {
let keywords = [
"if", "else if", "match", "while", "for", "loop", "&&", "||", "?",
];
keywords
.iter()
.map(|&keyword| line.matches(keyword).count())
.sum()
}
fn count_cognitive_complexity(&self, line: &str, nesting_level: i32) -> usize {
let mut complexity = 0;
let nesting_multiplier = (nesting_level as usize).max(1);
if line.contains("if") {
complexity += nesting_multiplier;
}
if line.contains("else") {
complexity += 1;
}
if line.contains("match") {
complexity += nesting_multiplier;
}
if line.contains("while") {
complexity += nesting_multiplier;
}
if line.contains("for") {
complexity += nesting_multiplier;
}
if line.contains("loop") {
complexity += nesting_multiplier;
}
complexity += line.matches("&&").count() * nesting_multiplier;
complexity += line.matches("||").count() * nesting_multiplier;
if line.contains("self.") && line.contains("(") {
complexity += 1;
}
complexity
}
fn count_function_parameters(&self, line: &str) -> usize {
if let Some(start) = line.find('(') {
if let Some(end) = line.rfind(')') {
if end > start {
let params_str = &line[start + 1..end];
if params_str.trim().is_empty() {
return 0;
}
let param_count = params_str.split(',').count();
if params_str.contains("self") {
return param_count.saturating_sub(1);
}
return param_count;
}
}
}
0
}
fn detect_structure(&self, line: &str) -> Option<(StructureType, String, Visibility)> {
let visibility = if line.starts_with("pub ") {
Visibility::Public
} else {
Visibility::Private
};
if line.contains("struct ") {
if let Some(name) = self.extract_structure_name(line, "struct ") {
return Some((StructureType::Struct, name, visibility));
}
}
if line.contains("enum ") {
if let Some(name) = self.extract_structure_name(line, "enum ") {
return Some((StructureType::Enum, name, visibility));
}
}
if line.contains("trait ") {
if let Some(name) = self.extract_structure_name(line, "trait ") {
return Some((StructureType::Trait, name, visibility));
}
}
if line.contains("impl ") {
if let Some(name) = self.extract_structure_name(line, "impl ") {
return Some((StructureType::Class, name, visibility)); }
}
if line.contains("mod ") {
if let Some(name) = self.extract_structure_name(line, "mod ") {
return Some((StructureType::Module, name, visibility));
}
}
None
}
fn extract_structure_name(&self, line: &str, keyword: &str) -> Option<String> {
if let Some(start) = line.find(keyword) {
let after_keyword = &line[start + keyword.len()..];
let name_part = after_keyword.split_whitespace().next()?;
let name = name_part.split('<').next()?.split('{').next()?.trim();
if !name.is_empty() {
Some(name.to_string())
} else {
None
}
} else {
None
}
}
}
impl LanguageAnalyzer for RustAnalyzer {
fn analyze_functions(&self, lines: &[String]) -> Result<Vec<FunctionInfo>> {
let mut functions = Vec::new();
let mut current_function: Option<FunctionInfo> = None;
let mut brace_count = 0;
let mut in_function = false;
for (line_num, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed.starts_with("//") || trimmed.is_empty() {
continue;
}
if trimmed.starts_with("fn ") || trimmed.contains(" fn ") {
if let Some(func_name) = self.extract_function_name(trimmed) {
current_function = Some(FunctionInfo {
name: func_name,
line_count: 0,
cyclomatic_complexity: 1, cognitive_complexity: 1, nesting_depth: 0,
parameter_count: 0,
return_path_count: 0,
start_line: line_num + 1,
end_line: line_num + 1,
is_method: false,
parent_class: None,
local_variable_count: 0,
has_recursion: false,
has_exception_handling: false,
visibility: Visibility::Public,
});
in_function = true;
brace_count = 0;
}
}
if in_function {
if let Some(ref mut func) = current_function {
func.line_count += 1;
func.end_line = line_num + 1;
let open_braces = trimmed.matches('{').count();
let close_braces = trimmed.matches('}').count();
brace_count += open_braces as i32 - close_braces as i32;
func.nesting_depth = func.nesting_depth.max(brace_count.max(0) as usize);
func.cyclomatic_complexity += self.count_complexity_keywords(trimmed);
func.cognitive_complexity +=
self.count_cognitive_complexity(trimmed, brace_count);
if trimmed.contains('(') && func.parameter_count == 0 {
func.parameter_count = self.count_function_parameters(trimmed);
}
if trimmed.contains("return") {
func.return_path_count += 1;
}
if trimmed.contains(&func.name) && !trimmed.starts_with("fn ") {
func.has_recursion = true;
}
if trimmed.contains("try") || trimmed.contains("catch") || trimmed.contains("?")
{
func.has_exception_handling = true;
}
if brace_count <= 0 && close_braces > 0 {
functions.push(func.clone());
current_function = None;
in_function = false;
}
}
}
}
Ok(functions)
}
fn analyze_structures(&self, lines: &[String]) -> Result<Vec<StructureInfo>> {
let mut structures = Vec::new();
let mut current_structure: Option<StructureInfo> = None;
let mut brace_count = 0;
let mut in_structure = false;
for (line_num, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed.starts_with("//") || trimmed.is_empty() {
continue;
}
if let Some((structure_type, name, visibility)) = self.detect_structure(trimmed) {
current_structure = Some(StructureInfo {
name,
structure_type,
line_count: 0,
start_line: line_num + 1,
end_line: line_num + 1,
methods: Vec::new(),
properties: 0,
visibility,
inheritance_depth: 0,
interface_count: 0,
});
in_structure = true;
brace_count = 0;
}
if in_structure {
if let Some(ref mut structure) = current_structure {
structure.line_count += 1;
structure.end_line = line_num + 1;
let open_braces = trimmed.matches('{').count();
let close_braces = trimmed.matches('}').count();
brace_count += open_braces as i32 - close_braces as i32;
if trimmed.contains(':') && !trimmed.contains("fn ") && !trimmed.contains("//")
{
structure.properties += 1;
}
if brace_count <= 0 && close_braces > 0 {
structures.push(structure.clone());
current_structure = None;
in_structure = false;
}
}
}
}
Ok(structures)
}
}
impl Default for RustAnalyzer {
fn default() -> Self {
Self::new()
}
}