use crate::core::{FileMetrics, FunctionMetrics};
use crate::refactoring::{
ConcernMixingPattern, DetectedPattern, PatternAssessment, PatternEvidence, PatternMatcher,
PatternType, SeparationDifficulty, Urgency,
};
pub struct IoWithLogicMatcher;
impl IoWithLogicMatcher {
fn indicates_io_operation(name: &str) -> bool {
const IO_INDICATORS: &[&str] = &["read", "write", "save", "load", "fetch", "send"];
IO_INDICATORS
.iter()
.any(|indicator| name.contains(indicator))
}
fn determine_separation_difficulty(cyclomatic: u32) -> SeparationDifficulty {
if cyclomatic > 10 {
SeparationDifficulty::High
} else {
SeparationDifficulty::Medium
}
}
fn determine_urgency(cyclomatic: u32) -> Urgency {
if cyclomatic > 10 {
Urgency::High
} else {
Urgency::Medium
}
}
}
impl PatternMatcher for IoWithLogicMatcher {
fn match_pattern(
&self,
function: &FunctionMetrics,
_file: &FileMetrics,
) -> Option<DetectedPattern> {
let has_io = Self::indicates_io_operation(&function.name);
let has_complex_logic = function.cyclomatic > 5;
if has_io && has_complex_logic {
Some(DetectedPattern {
pattern_type: PatternType::MixedConcerns(ConcernMixingPattern {
concerns: vec!["I/O Operations".to_string(), "Business Logic".to_string()],
separation_difficulty: Self::determine_separation_difficulty(
function.cyclomatic,
),
}),
confidence: 0.85,
evidence: PatternEvidence {
code_snippets: vec![],
line_numbers: vec![function.line as u32],
confidence_factors: vec![
"Function name indicates I/O operations".to_string(),
format!(
"High complexity {} suggests business logic",
function.cyclomatic
),
],
},
assessment: PatternAssessment::AntiPattern {
problems: vec![
"Mixing I/O with business logic".to_string(),
"Cannot test logic without mocking I/O".to_string(),
"Violates functional core / imperative shell".to_string(),
],
recommended_patterns: vec![PatternType::FunctionalComposition],
urgency: Self::determine_urgency(function.cyclomatic),
},
})
} else {
None
}
}
}
pub struct PureIoMatcher;
impl PureIoMatcher {
fn is_io_function(name: &str) -> bool {
const IO_INDICATORS: &[&str] = &["read", "write", "save", "load"];
IO_INDICATORS
.iter()
.any(|indicator| name.contains(indicator))
}
fn is_simple_complexity(cyclomatic: u32) -> bool {
cyclomatic <= 2
}
}
impl PatternMatcher for PureIoMatcher {
fn match_pattern(
&self,
function: &FunctionMetrics,
_file: &FileMetrics,
) -> Option<DetectedPattern> {
let has_io = Self::is_io_function(&function.name);
let is_simple = Self::is_simple_complexity(function.cyclomatic);
if has_io && is_simple {
Some(DetectedPattern {
pattern_type: PatternType::IOOrchestration(
crate::refactoring::OrchestrationPattern {
pattern_type: "PureIO".to_string(),
description: "Simple I/O without business logic".to_string(),
},
),
confidence: 0.9,
evidence: PatternEvidence {
code_snippets: vec![],
line_numbers: vec![function.line as u32],
confidence_factors: vec![
"I/O operation with minimal complexity".to_string(),
"Follows imperative shell pattern".to_string(),
],
},
assessment: PatternAssessment::GoodExample {
strengths: vec![
"Clean separation of I/O from logic".to_string(),
"Simple, focused responsibility".to_string(),
"Easy to mock for testing".to_string(),
],
why_good: "I/O functions should be simple wrappers at boundaries".to_string(),
},
})
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{ComplexityMetrics, FileMetrics, FunctionMetrics, Language};
use crate::refactoring::patterns::PatternMatcher;
use std::path::PathBuf;
fn create_test_function(name: &str, cyclomatic: u32) -> FunctionMetrics {
FunctionMetrics {
name: name.to_string(),
file: PathBuf::from("test.rs"),
line: 1,
cyclomatic,
cognitive: 0,
nesting: 0,
length: 10,
is_test: false,
visibility: None,
is_trait_method: false,
in_test_module: false,
entropy_score: None,
is_pure: None,
purity_confidence: None,
purity_reason: None,
call_dependencies: None,
detected_patterns: None,
upstream_callers: None,
downstream_callees: None,
mapping_pattern_result: None,
adjusted_complexity: None,
composition_metrics: None,
language_specific: None,
purity_level: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
}
}
fn create_test_file() -> FileMetrics {
FileMetrics {
path: PathBuf::from("test.rs"),
language: Language::Rust,
complexity: ComplexityMetrics::default(),
debt_items: vec![],
dependencies: vec![],
duplications: vec![],
total_lines: 0,
module_scope: None,
classes: None,
}
}
#[test]
fn test_is_io_function_detects_read() {
assert!(PureIoMatcher::is_io_function("read_file"));
assert!(PureIoMatcher::is_io_function("file_reader"));
assert!(PureIoMatcher::is_io_function("async_read"));
}
#[test]
fn test_is_io_function_detects_write() {
assert!(PureIoMatcher::is_io_function("write_data"));
assert!(PureIoMatcher::is_io_function("file_writer"));
assert!(PureIoMatcher::is_io_function("buffer_write"));
}
#[test]
fn test_is_io_function_detects_save_and_load() {
assert!(PureIoMatcher::is_io_function("save_config"));
assert!(PureIoMatcher::is_io_function("load_settings"));
assert!(PureIoMatcher::is_io_function("autosave"));
assert!(PureIoMatcher::is_io_function("preload_cache"));
}
#[test]
fn test_is_io_function_rejects_non_io() {
assert!(!PureIoMatcher::is_io_function("calculate_sum"));
assert!(!PureIoMatcher::is_io_function("process_data"));
assert!(!PureIoMatcher::is_io_function("validate_input"));
assert!(!PureIoMatcher::is_io_function("transform_result"));
}
#[test]
fn test_is_simple_complexity() {
assert!(PureIoMatcher::is_simple_complexity(0));
assert!(PureIoMatcher::is_simple_complexity(1));
assert!(PureIoMatcher::is_simple_complexity(2));
assert!(!PureIoMatcher::is_simple_complexity(3));
assert!(!PureIoMatcher::is_simple_complexity(10));
}
#[test]
fn test_match_pattern_detects_pure_io() {
let matcher = PureIoMatcher;
let function = create_test_function("read_file", 1);
let file = create_test_file();
let result = matcher.match_pattern(&function, &file);
assert!(result.is_some());
let pattern = result.unwrap();
assert_eq!(pattern.confidence, 0.9);
if let crate::refactoring::PatternType::IOOrchestration(orch) = pattern.pattern_type {
assert_eq!(orch.pattern_type, "PureIO");
} else {
panic!("Expected IOOrchestration pattern");
}
}
#[test]
fn test_match_pattern_rejects_complex_io() {
let matcher = PureIoMatcher;
let function = create_test_function("read_and_process_file", 5);
let file = create_test_file();
let result = matcher.match_pattern(&function, &file);
assert!(result.is_none());
}
#[test]
fn test_match_pattern_rejects_non_io_simple() {
let matcher = PureIoMatcher;
let function = create_test_function("calculate_sum", 1);
let file = create_test_file();
let result = matcher.match_pattern(&function, &file);
assert!(result.is_none());
}
#[test]
fn test_match_pattern_boundary_complexity() {
let matcher = PureIoMatcher;
let file = create_test_file();
let function = create_test_function("write_buffer", 2);
let result = matcher.match_pattern(&function, &file);
assert!(result.is_some());
let function = create_test_function("write_buffer", 3);
let result = matcher.match_pattern(&function, &file);
assert!(result.is_none());
}
}