Skip to main content
Glama

In Memoria

implementation.rs38.3 kB
//! Implementation pattern recognition for design patterns #[cfg(feature = "napi-bindings")] use napi_derive::napi; use crate::patterns::types::{Pattern, PatternExample, ImplementationPattern, PatternExtractor}; use crate::types::{ParseError, LineRange, SemanticConcept}; use std::collections::HashMap; use regex::Regex; use walkdir::WalkDir; use std::fs; /// Analyzer for detecting implementation patterns (design patterns) #[cfg_attr(feature = "napi-bindings", napi)] pub struct ImplementationPatternAnalyzer { patterns: HashMap<String, ImplementationPattern>, pattern_signatures: HashMap<String, PatternSignature>, } #[derive(Debug, Clone)] struct PatternSignature { required_methods: Vec<String>, optional_methods: Vec<String>, class_characteristics: Vec<String>, code_patterns: Vec<String>, confidence_threshold: f64, } #[derive(Debug, Clone)] struct PatternMatch { pattern_name: String, confidence: f64, evidence: Vec<String>, location: String, } #[cfg_attr(feature = "napi-bindings", napi)] impl ImplementationPatternAnalyzer { #[cfg_attr(feature = "napi-bindings", napi(constructor))] pub fn new() -> Self { let mut analyzer = ImplementationPatternAnalyzer { patterns: HashMap::new(), pattern_signatures: HashMap::new(), }; analyzer.initialize_pattern_signatures(); analyzer } /// Initialize signatures for common design patterns fn initialize_pattern_signatures(&mut self) { // Singleton Pattern self.pattern_signatures.insert("Singleton".to_string(), PatternSignature { required_methods: vec!["getInstance".to_string()], optional_methods: vec!["constructor".to_string(), "__construct".to_string()], class_characteristics: vec!["static_instance".to_string(), "private_constructor".to_string()], code_patterns: vec![ r"private\s+static\s+\w*instance".to_string(), r"getInstance\(\)".to_string(), r"private\s+\w*\(\)".to_string(), // private constructor ], confidence_threshold: 0.7, }); // Factory Pattern self.pattern_signatures.insert("Factory".to_string(), PatternSignature { required_methods: vec!["create".to_string(), "make".to_string(), "build".to_string()], optional_methods: vec!["factory".to_string()], class_characteristics: vec!["creator".to_string(), "product".to_string()], code_patterns: vec![ r"create\w*\(\)".to_string(), r"make\w*\(\)".to_string(), r"Factory".to_string(), ], confidence_threshold: 0.6, }); // Observer Pattern self.pattern_signatures.insert("Observer".to_string(), PatternSignature { required_methods: vec!["notify".to_string(), "update".to_string(), "subscribe".to_string()], optional_methods: vec!["unsubscribe".to_string(), "addListener".to_string(), "removeListener".to_string()], class_characteristics: vec!["subject".to_string(), "observer".to_string(), "listeners".to_string()], code_patterns: vec![ r"notify\w*\(\)".to_string(), r"update\(\)".to_string(), r"subscribe\(\)".to_string(), r"addEventListener".to_string(), ], confidence_threshold: 0.7, }); // Builder Pattern self.pattern_signatures.insert("Builder".to_string(), PatternSignature { required_methods: vec!["build".to_string(), "with".to_string(), "set".to_string()], optional_methods: vec!["create".to_string(), "builder".to_string()], class_characteristics: vec!["builder".to_string(), "director".to_string()], code_patterns: vec![ r"\.with\w+\(".to_string(), r"\.set\w+\(".to_string(), r"\.build\(\)".to_string(), r"Builder".to_string(), ], confidence_threshold: 0.6, }); // Strategy Pattern self.pattern_signatures.insert("Strategy".to_string(), PatternSignature { required_methods: vec!["execute".to_string(), "apply".to_string(), "process".to_string()], optional_methods: vec!["strategy".to_string(), "algorithm".to_string()], class_characteristics: vec!["strategy".to_string(), "context".to_string()], code_patterns: vec![ r"execute\(\)".to_string(), r"Strategy".to_string(), r"setStrategy\(".to_string(), ], confidence_threshold: 0.6, }); // Dependency Injection Pattern self.pattern_signatures.insert("DependencyInjection".to_string(), PatternSignature { required_methods: vec!["inject".to_string(), "provide".to_string(), "register".to_string()], optional_methods: vec!["bind".to_string(), "container".to_string()], class_characteristics: vec!["injector".to_string(), "container".to_string(), "provider".to_string()], code_patterns: vec![ r"@inject".to_string(), r"@Injectable".to_string(), r"container\.get\(".to_string(), r"DI".to_string(), ], confidence_threshold: 0.7, }); // Decorator Pattern self.pattern_signatures.insert("Decorator".to_string(), PatternSignature { required_methods: vec!["wrap".to_string(), "decorate".to_string()], optional_methods: vec!["unwrap".to_string()], class_characteristics: vec!["decorator".to_string(), "wrapper".to_string()], code_patterns: vec![ r"@\w+".to_string(), // Decorator syntax r"Decorator".to_string(), r"wrap\(".to_string(), ], confidence_threshold: 0.6, }); // Command Pattern self.pattern_signatures.insert("Command".to_string(), PatternSignature { required_methods: vec!["execute".to_string(), "undo".to_string()], optional_methods: vec!["redo".to_string(), "command".to_string()], class_characteristics: vec!["command".to_string(), "invoker".to_string(), "receiver".to_string()], code_patterns: vec![ r"execute\(\)".to_string(), r"undo\(\)".to_string(), r"Command".to_string(), ], confidence_threshold: 0.7, }); // Adapter Pattern self.pattern_signatures.insert("Adapter".to_string(), PatternSignature { required_methods: vec!["adapt".to_string(), "convert".to_string()], optional_methods: vec!["wrap".to_string()], class_characteristics: vec!["adapter".to_string(), "adaptee".to_string()], code_patterns: vec![ r"Adapter".to_string(), r"adapt\(".to_string(), ], confidence_threshold: 0.6, }); } /// Analyze semantic concepts for implementation patterns pub fn analyze_concepts(&mut self, concepts: &[SemanticConcept]) -> Result<Vec<Pattern>, ParseError> { let mut detected_patterns = Vec::new(); let pattern_matches = self.detect_patterns_in_concepts(concepts)?; for pattern_match in pattern_matches { if pattern_match.confidence >= self.pattern_signatures .get(&pattern_match.pattern_name) .map(|s| s.confidence_threshold) .unwrap_or(0.5) { let examples = self.create_examples_for_pattern(&pattern_match, concepts); let pattern = Pattern { id: format!("implementation_{}", pattern_match.pattern_name.to_lowercase()), pattern_type: "implementation".to_string(), description: format!( "{} pattern detected with {:.1}% confidence", pattern_match.pattern_name, pattern_match.confidence * 100.0 ), frequency: pattern_match.evidence.len() as u32, confidence: pattern_match.confidence, examples, contexts: vec!["design_pattern".to_string()], }; detected_patterns.push(pattern); // Store in internal patterns let impl_pattern = ImplementationPattern { pattern_type: pattern_match.pattern_name.clone(), frequency: pattern_match.evidence.len() as u32, code_signatures: pattern_match.evidence.clone(), confidence: pattern_match.confidence, }; self.patterns.insert(pattern_match.pattern_name.clone(), impl_pattern); } } Ok(detected_patterns) } /// Detect anti-patterns in implementation pub fn detect_antipatterns(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); antipatterns.extend(self.detect_god_object_antipattern(concepts)); antipatterns.extend(self.detect_spaghetti_code_antipattern(concepts)); antipatterns.extend(self.detect_copy_paste_antipattern(concepts)); antipatterns.extend(self.detect_magic_number_antipattern(concepts)); antipatterns.extend(self.detect_long_parameter_list_antipattern(concepts)); antipatterns } /// Generate recommendations based on detected patterns pub fn generate_recommendations(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut recommendations = Vec::new(); // Check for missing patterns that could be beneficial if self.should_suggest_singleton(concepts) { recommendations.push("Consider using Singleton pattern for global state management".to_string()); } if self.should_suggest_factory(concepts) { recommendations.push("Consider using Factory pattern for object creation complexity".to_string()); } if self.should_suggest_observer(concepts) { recommendations.push("Consider using Observer pattern for event handling".to_string()); } if self.should_suggest_strategy(concepts) { recommendations.push("Consider using Strategy pattern to reduce conditional complexity".to_string()); } if self.should_suggest_dependency_injection(concepts) { recommendations.push("Consider using Dependency Injection for better testability".to_string()); } // Anti-pattern recommendations let antipatterns = self.detect_antipatterns(concepts); if !antipatterns.is_empty() { recommendations.push("Address detected anti-patterns to improve code quality".to_string()); } if recommendations.is_empty() { recommendations.push("Implementation patterns look good! Consider documenting design decisions".to_string()); } recommendations } /// Analyze code files for pattern signatures pub fn analyze_code_files(&mut self, path: &str) -> Result<Vec<Pattern>, ParseError> { let mut detected_patterns = Vec::new(); for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { if entry.file_type().is_file() { let file_path = entry.path(); if let Some(extension) = file_path.extension().and_then(|s| s.to_str()) { if matches!(extension.to_lowercase().as_str(), "js" | "ts" | "jsx" | "tsx" | "rs" | "py" | "java" | "cs" | "cpp" | "c") { if let Ok(content) = fs::read_to_string(file_path) { let patterns = self.detect_patterns_in_code(&content, file_path.to_string_lossy().as_ref())?; detected_patterns.extend(patterns); } } } } } Ok(detected_patterns) } /// Detect patterns in concepts using semantic analysis fn detect_patterns_in_concepts(&self, concepts: &[SemanticConcept]) -> Result<Vec<PatternMatch>, ParseError> { let mut pattern_matches = Vec::new(); for (pattern_name, signature) in &self.pattern_signatures { let mut evidence = Vec::new(); let mut confidence_scores = Vec::new(); // Check for required methods in concepts let method_matches = self.find_method_matches(concepts, &signature.required_methods); evidence.extend(method_matches.iter().map(|m| format!("Method: {}", m))); if !method_matches.is_empty() { confidence_scores.push(0.4 * (method_matches.len() as f64 / signature.required_methods.len() as f64)); } // Check for optional methods (bonus confidence) if !signature.optional_methods.is_empty() { let optional_matches = self.find_method_matches(concepts, &signature.optional_methods); evidence.extend(optional_matches.iter().map(|m| format!("Optional Method: {}", m))); if !optional_matches.is_empty() { // Optional methods provide a confidence boost but aren't required confidence_scores.push(0.15 * (optional_matches.len() as f64 / signature.optional_methods.len() as f64)); } } // Check for class characteristics let class_matches = self.find_class_characteristic_matches(concepts, &signature.class_characteristics); evidence.extend(class_matches.iter().map(|c| format!("Characteristic: {}", c))); if !class_matches.is_empty() { confidence_scores.push(0.3 * (class_matches.len() as f64 / signature.class_characteristics.len() as f64)); } // Check naming patterns let naming_matches = self.find_naming_pattern_matches(concepts, pattern_name); evidence.extend(naming_matches.iter().map(|n| format!("Naming: {}", n))); if !naming_matches.is_empty() { confidence_scores.push(0.3); } let total_confidence: f64 = confidence_scores.iter().sum(); if total_confidence >= signature.confidence_threshold && !evidence.is_empty() { pattern_matches.push(PatternMatch { pattern_name: pattern_name.clone(), confidence: total_confidence, evidence, location: "multiple_concepts".to_string(), }); } } Ok(pattern_matches) } /// Detect patterns in code using regex patterns fn detect_patterns_in_code(&self, code: &str, file_path: &str) -> Result<Vec<Pattern>, ParseError> { let mut detected_patterns = Vec::new(); for (pattern_name, signature) in &self.pattern_signatures { let mut evidence = Vec::new(); let mut confidence = 0.0; // Check code patterns using regex for code_pattern in &signature.code_patterns { if let Ok(regex) = Regex::new(code_pattern) { let matches: Vec<_> = regex.find_iter(code).collect(); if !matches.is_empty() { evidence.extend(matches.iter().map(|m| format!("Code pattern: {}", m.as_str()))); confidence += 0.2; } } } // Check for method names in the code for method in &signature.required_methods { if code.contains(method) { evidence.push(format!("Method found: {}", method)); confidence += 0.2; } } if confidence >= signature.confidence_threshold && !evidence.is_empty() { let examples = vec![PatternExample { code: evidence.join(", "), file_path: file_path.to_string(), line_range: LineRange { start: 1, end: 1 }, }]; detected_patterns.push(Pattern { id: format!("implementation_{}", pattern_name.to_lowercase()), pattern_type: "implementation".to_string(), description: format!("{} pattern detected in code", pattern_name), frequency: evidence.len() as u32, confidence, examples, contexts: vec!["code_analysis".to_string()], }); } } Ok(detected_patterns) } /// Find method matches in concepts fn find_method_matches(&self, concepts: &[SemanticConcept], required_methods: &[String]) -> Vec<String> { let mut matches = Vec::new(); for concept in concepts { if concept.concept_type == "method" || concept.concept_type == "function" { for required_method in required_methods { if concept.name.to_lowercase().contains(&required_method.to_lowercase()) || self.is_method_variant(&concept.name, required_method) { matches.push(concept.name.clone()); break; } } } } matches } /// Find class characteristic matches fn find_class_characteristic_matches(&self, concepts: &[SemanticConcept], characteristics: &[String]) -> Vec<String> { let mut matches = Vec::new(); for concept in concepts { for characteristic in characteristics { if concept.name.to_lowercase().contains(&characteristic.to_lowercase()) || concept.concept_type.to_lowercase().contains(&characteristic.to_lowercase()) || concept.metadata.values().any(|v| v.to_lowercase().contains(&characteristic.to_lowercase())) { matches.push(characteristic.clone()); } } } matches } /// Find naming pattern matches fn find_naming_pattern_matches(&self, concepts: &[SemanticConcept], pattern_name: &str) -> Vec<String> { let mut matches = Vec::new(); let pattern_lower = pattern_name.to_lowercase(); for concept in concepts { if concept.name.to_lowercase().contains(&pattern_lower) || concept.file_path.to_lowercase().contains(&pattern_lower) { matches.push(concept.name.clone()); } } matches } /// Check if a method name is a variant of a required method fn is_method_variant(&self, method_name: &str, required_method: &str) -> bool { let method_lower = method_name.to_lowercase(); let required_lower = required_method.to_lowercase(); // Check for common variants match required_lower.as_str() { "getinstance" => method_lower.contains("getinstance") || method_lower.contains("instance"), "create" => method_lower.contains("create") || method_lower.contains("new") || method_lower.contains("make"), "notify" => method_lower.contains("notify") || method_lower.contains("emit") || method_lower.contains("trigger"), "update" => method_lower.contains("update") || method_lower.contains("refresh") || method_lower.contains("change"), "build" => method_lower.contains("build") || method_lower.contains("construct") || method_lower.contains("assemble"), _ => method_lower.contains(&required_lower), } } /// Create examples for detected patterns fn create_examples_for_pattern(&self, pattern_match: &PatternMatch, concepts: &[SemanticConcept]) -> Vec<PatternExample> { let mut examples = Vec::new(); // Find concepts that contributed to this pattern match for concept in concepts.iter().take(3) { // Limit to first 3 examples if concept.name.to_lowercase().contains(&pattern_match.pattern_name.to_lowercase()) || pattern_match.evidence.iter().any(|e| e.contains(&concept.name)) { examples.push(PatternExample { code: format!("{} {}", concept.concept_type, concept.name), file_path: concept.file_path.clone(), line_range: concept.line_range.clone(), }); } } if examples.is_empty() { // Fallback example examples.push(PatternExample { code: pattern_match.evidence.first().cloned().unwrap_or_else(|| "Pattern detected".to_string()), file_path: pattern_match.location.clone(), line_range: LineRange { start: 1, end: 1 }, }); } examples } // Anti-pattern detection methods fn detect_god_object_antipattern(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); let mut class_method_counts: HashMap<String, usize> = HashMap::new(); for concept in concepts { if concept.concept_type == "class" { let method_count = concepts.iter() .filter(|c| c.concept_type == "method" && c.file_path == concept.file_path) .count(); class_method_counts.insert(concept.name.clone(), method_count); if method_count > 20 { antipatterns.push(format!( "God Object anti-pattern: Class '{}' has {} methods ({}:{})", concept.name, method_count, concept.file_path, concept.line_range.start )); } } } antipatterns } fn detect_spaghetti_code_antipattern(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); // Check for functions with too many dependencies for concept in concepts { if concept.concept_type == "function" || concept.concept_type == "method" { let dependency_count = concept.relationships.len(); if dependency_count > 15 { antipatterns.push(format!( "Spaghetti Code: Function '{}' has {} dependencies ({}:{})", concept.name, dependency_count, concept.file_path, concept.line_range.start )); } } } antipatterns } fn detect_copy_paste_antipattern(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); let mut similar_names: HashMap<String, Vec<&SemanticConcept>> = HashMap::new(); // Group concepts by similar names for concept in concepts { let name_base = self.extract_name_base(&concept.name); similar_names.entry(name_base).or_default().push(concept); } // Check for potential copy-paste patterns for (base_name, group) in similar_names { if group.len() > 3 && base_name.len() > 3 { let names: Vec<String> = group.iter().map(|c| c.name.clone()).collect(); antipatterns.push(format!( "Potential Copy-Paste: {} similar functions found: {}", group.len(), names.join(", ") )); } } antipatterns } fn detect_magic_number_antipattern(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); // This is a simplified check - in practice you'd analyze the actual code for concept in concepts { if concept.concept_type == "constant" { // Check if it's a meaningful constant name or just a number if concept.name.chars().all(|c| c.is_numeric() || c == '.' || c == '_') { antipatterns.push(format!( "Magic Number: Constant '{}' should have a descriptive name ({}:{})", concept.name, concept.file_path, concept.line_range.start )); } } } antipatterns } fn detect_long_parameter_list_antipattern(&self, concepts: &[SemanticConcept]) -> Vec<String> { let mut antipatterns = Vec::new(); for concept in concepts { if concept.concept_type == "function" || concept.concept_type == "method" { if let Some(params) = concept.metadata.get("parameters") { if let Ok(param_count) = params.parse::<usize>() { if param_count > 5 { antipatterns.push(format!( "Long Parameter List: Function '{}' has {} parameters ({}:{})", concept.name, param_count, concept.file_path, concept.line_range.start )); } } } } } antipatterns } // Recommendation helper methods fn should_suggest_singleton(&self, concepts: &[SemanticConcept]) -> bool { // Check for global state or configuration management let has_global_state = concepts.iter().any(|c| c.name.to_lowercase().contains("config") || c.name.to_lowercase().contains("settings") || c.name.to_lowercase().contains("global") ); let has_singleton = self.patterns.contains_key("Singleton"); has_global_state && !has_singleton } fn should_suggest_factory(&self, concepts: &[SemanticConcept]) -> bool { // Check for complex object creation let creation_complexity = concepts.iter() .filter(|c| c.concept_type == "constructor" || c.name.to_lowercase().contains("new")) .count(); let has_factory = self.patterns.contains_key("Factory"); creation_complexity > 3 && !has_factory } fn should_suggest_observer(&self, concepts: &[SemanticConcept]) -> bool { // Check for event handling patterns let has_events = concepts.iter().any(|c| c.name.to_lowercase().contains("event") || c.name.to_lowercase().contains("listener") || c.name.to_lowercase().contains("callback") ); let has_observer = self.patterns.contains_key("Observer"); has_events && !has_observer } fn should_suggest_strategy(&self, concepts: &[SemanticConcept]) -> bool { // Check for conditional complexity let has_complex_conditionals = concepts.iter().any(|c| c.metadata.get("body").is_some_and(|body| body.matches("if").count() > 5 || body.matches("switch").count() > 1 ) ); let has_strategy = self.patterns.contains_key("Strategy"); has_complex_conditionals && !has_strategy } fn should_suggest_dependency_injection(&self, concepts: &[SemanticConcept]) -> bool { // Check for tight coupling let high_coupling_count = concepts.iter() .filter(|c| c.relationships.len() > 8) .count(); let has_di = self.patterns.contains_key("DependencyInjection"); high_coupling_count > 2 && !has_di } /// Extract base name for similarity detection fn extract_name_base(&self, name: &str) -> String { // Remove common suffixes and prefixes let mut base = name.to_lowercase(); // Remove numbers at the end while base.chars().last().is_some_and(|c| c.is_numeric()) { base.pop(); } // Remove common suffixes for suffix in &["test", "impl", "service", "controller", "handler"] { if base.ends_with(suffix) { base = base[..base.len() - suffix.len()].to_string(); break; } } base } } impl PatternExtractor for ImplementationPatternAnalyzer { fn extract_patterns(&self, path: &str) -> Result<Vec<Pattern>, ParseError> { let mut analyzer = self.clone(); analyzer.analyze_code_files(path) } } impl Clone for ImplementationPatternAnalyzer { fn clone(&self) -> Self { ImplementationPatternAnalyzer { patterns: self.patterns.clone(), pattern_signatures: self.pattern_signatures.clone(), } } } impl Default for ImplementationPatternAnalyzer { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use std::collections::HashMap; fn create_test_concept(name: &str, concept_type: &str, file_path: &str) -> SemanticConcept { SemanticConcept { id: format!("test_{}", name), name: name.to_string(), concept_type: concept_type.to_string(), confidence: 0.8, file_path: file_path.to_string(), line_range: LineRange { start: 1, end: 10 }, relationships: HashMap::new(), metadata: HashMap::new(), } } #[test] fn test_implementation_pattern_analyzer_creation() { let analyzer = ImplementationPatternAnalyzer::new(); assert!(!analyzer.pattern_signatures.is_empty()); assert!(analyzer.pattern_signatures.contains_key("Singleton")); assert!(analyzer.pattern_signatures.contains_key("Factory")); assert!(analyzer.pattern_signatures.contains_key("Observer")); } #[test] fn test_singleton_pattern_detection() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("getInstance", "method", "Singleton.js"), create_test_concept("SingletonClass", "class", "Singleton.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); let singleton_pattern = patterns.iter().find(|p| p.id.contains("singleton")); assert!(singleton_pattern.is_some()); if let Some(pattern) = singleton_pattern { assert_eq!(pattern.pattern_type, "implementation"); assert!(pattern.confidence > 0.0); } } #[test] fn test_factory_pattern_detection() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("createUser", "method", "UserFactory.js"), create_test_concept("UserFactory", "class", "UserFactory.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); let factory_pattern = patterns.iter().find(|p| p.id.contains("factory")); assert!(factory_pattern.is_some()); } #[test] fn test_observer_pattern_detection() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("notify", "method", "Observable.js"), create_test_concept("subscribe", "method", "Observable.js"), create_test_concept("update", "method", "Observer.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); let observer_pattern = patterns.iter().find(|p| p.id.contains("observer")); assert!(observer_pattern.is_some()); } #[test] fn test_builder_pattern_detection() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("withName", "method", "UserBuilder.js"), create_test_concept("setAge", "method", "UserBuilder.js"), create_test_concept("build", "method", "UserBuilder.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); let builder_pattern = patterns.iter().find(|p| p.id.contains("builder")); assert!(builder_pattern.is_some()); } #[test] fn test_god_object_antipattern_detection() { let analyzer = ImplementationPatternAnalyzer::new(); let mut concepts = vec![create_test_concept("GodClass", "class", "GodClass.js")]; // Add many methods to simulate God Object for i in 0..25 { concepts.push(create_test_concept(&format!("method{}", i), "method", "GodClass.js")); } let antipatterns = analyzer.detect_god_object_antipattern(&concepts); assert!(!antipatterns.is_empty()); assert!(antipatterns[0].contains("God Object")); } #[test] fn test_spaghetti_code_antipattern_detection() { let analyzer = ImplementationPatternAnalyzer::new(); let mut concept = create_test_concept("complexFunction", "function", "test.js"); // Add many dependencies to simulate spaghetti code for i in 0..20 { concept.relationships.insert(format!("depends_{}", i), format!("dependency{}", i)); } let concepts = vec![concept]; let antipatterns = analyzer.detect_spaghetti_code_antipattern(&concepts); assert!(!antipatterns.is_empty()); assert!(antipatterns[0].contains("Spaghetti Code")); } #[test] fn test_copy_paste_antipattern_detection() { let analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("processUser1", "function", "test.js"), create_test_concept("processUser2", "function", "test.js"), create_test_concept("processUser3", "function", "test.js"), create_test_concept("processUser4", "function", "test.js"), ]; let antipatterns = analyzer.detect_copy_paste_antipattern(&concepts); assert!(!antipatterns.is_empty()); assert!(antipatterns[0].contains("Copy-Paste")); } #[test] fn test_long_parameter_list_detection() { let analyzer = ImplementationPatternAnalyzer::new(); let mut concept = create_test_concept("complexFunction", "function", "test.js"); concept.metadata.insert("parameters".to_string(), "8".to_string()); let concepts = vec![concept]; let antipatterns = analyzer.detect_long_parameter_list_antipattern(&concepts); assert!(!antipatterns.is_empty()); assert!(antipatterns[0].contains("Long Parameter List")); } #[test] fn test_method_variant_detection() { let analyzer = ImplementationPatternAnalyzer::new(); assert!(analyzer.is_method_variant("getInstance", "getinstance")); assert!(analyzer.is_method_variant("createUser", "create")); assert!(analyzer.is_method_variant("notifyAll", "notify")); assert!(analyzer.is_method_variant("updateState", "update")); assert!(analyzer.is_method_variant("buildObject", "build")); assert!(!analyzer.is_method_variant("randomMethod", "create")); } #[test] fn test_recommendation_generation() { let analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("GlobalConfig", "class", "config.js"), create_test_concept("addEventListener", "function", "events.js"), ]; let recommendations = analyzer.generate_recommendations(&concepts); assert!(!recommendations.is_empty()); // Should suggest Singleton for global state assert!(recommendations.iter().any(|r| r.contains("Singleton"))); } #[test] fn test_name_base_extraction() { let analyzer = ImplementationPatternAnalyzer::new(); assert_eq!(analyzer.extract_name_base("processUser1"), "processuser"); assert_eq!(analyzer.extract_name_base("userServiceImpl"), "user"); assert_eq!(analyzer.extract_name_base("handleEventTest"), "handleevent"); assert_eq!(analyzer.extract_name_base("dataController"), "data"); } #[test] fn test_pattern_confidence_calculation() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ create_test_concept("getInstance", "method", "Singleton.js"), create_test_concept("SingletonInstance", "field", "Singleton.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); if let Some(pattern) = patterns.first() { assert!(pattern.confidence >= 0.5); assert!(pattern.confidence <= 1.0); } } #[test] fn test_multiple_pattern_detection() { let mut analyzer = ImplementationPatternAnalyzer::new(); let concepts = vec![ // Singleton pattern create_test_concept("getInstance", "method", "Singleton.js"), // Factory pattern create_test_concept("createUser", "method", "Factory.js"), // Observer pattern create_test_concept("notify", "method", "Observer.js"), create_test_concept("subscribe", "method", "Observer.js"), ]; let patterns = analyzer.analyze_concepts(&concepts).unwrap(); assert!(patterns.len() >= 2); let pattern_names: Vec<String> = patterns.iter().map(|p| p.id.clone()).collect(); assert!(pattern_names.iter().any(|name| name.contains("singleton") || name.contains("factory") || name.contains("observer"))); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pi22by7/In-Memoria'

If you have feedback or need assistance with the MCP directory API, please join our Discord server