#![cfg_attr(coverage_nightly, coverage(off))]
use super::language_detector::Language; use super::operators::MutationOperator;
use anyhow::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
#[async_trait]
pub trait LanguageAdapter: Send + Sync {
fn name(&self) -> &str;
fn extensions(&self) -> &[&str];
async fn parse(&self, source: &str) -> Result<String>;
async fn unparse(&self, ast: &str) -> Result<String>;
fn mutation_operators(&self) -> Vec<Box<dyn MutationOperator>>;
async fn run_tests(&self, source_file: &Path) -> Result<TestRunResult>;
}
#[derive(Debug, Clone)]
pub struct TestRunResult {
pub passed: bool,
pub failures: Vec<String>,
pub execution_time_ms: u64,
pub stdout: String,
pub stderr: String,
}
pub struct LanguageRegistry {
adapters: HashMap<String, Arc<dyn LanguageAdapter>>,
}
impl LanguageRegistry {
pub fn new() -> Self {
Self {
adapters: HashMap::new(),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register(&mut self, adapter: Arc<dyn LanguageAdapter>) {
self.adapters.insert(adapter.name().to_string(), adapter);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register_typescript(&mut self) {
use crate::services::mutation::TypeScriptAdapter;
self.register(Arc::new(TypeScriptAdapter::new()));
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register_python(&mut self) {
use crate::services::mutation::PythonAdapter;
self.register(Arc::new(PythonAdapter::new()));
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register_go(&mut self) {
use crate::services::mutation::GoAdapter;
self.register(Arc::new(GoAdapter::new()));
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register_cpp(&mut self) {
use crate::services::mutation::CppAdapter;
self.register(Arc::new(CppAdapter::new()));
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn register_wasm(&mut self) {
use crate::services::mutation::WasmAdapter;
self.register(Arc::new(WasmAdapter::new()));
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn detect_language(&self, path: &Path) -> Option<Arc<dyn LanguageAdapter>> {
let detected_language = Language::from_extension(path);
match detected_language {
Language::Rust => self.get_adapter("rust"),
Language::Python => self.get_adapter("python"),
Language::TypeScript | Language::JavaScript => self.get_adapter("typescript"),
Language::Go => self.get_adapter("go"),
Language::Cpp => self.get_adapter("cpp"),
Language::Unsupported => self.detect_language_by_extension(path),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn detect_language_by_extension(&self, path: &Path) -> Option<Arc<dyn LanguageAdapter>> {
let extension = path.extension()?.to_str()?;
for adapter in self.adapters.values() {
if adapter.extensions().contains(&extension) {
return Some(Arc::clone(adapter));
}
}
None
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn get_adapter(&self, name: &str) -> Option<Arc<dyn LanguageAdapter>> {
self.adapters.get(name).map(Arc::clone)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn languages(&self) -> Vec<&str> {
self.adapters.keys().map(|s| s.as_str()).collect()
}
}
impl Default for LanguageRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
struct MockAdapter;
#[async_trait]
impl LanguageAdapter for MockAdapter {
fn name(&self) -> &str {
"mock"
}
fn extensions(&self) -> &[&str] {
&["mock"]
}
async fn parse(&self, source: &str) -> Result<String> {
Ok(source.to_string())
}
async fn unparse(&self, ast: &str) -> Result<String> {
Ok(ast.to_string())
}
fn mutation_operators(&self) -> Vec<Box<dyn MutationOperator>> {
vec![]
}
async fn run_tests(&self, _source_file: &Path) -> Result<TestRunResult> {
Ok(TestRunResult {
passed: true,
failures: vec![],
execution_time_ms: 100,
stdout: String::new(),
stderr: String::new(),
})
}
}
#[test]
fn test_language_registry_register() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
assert_eq!(registry.languages(), vec!["mock"]);
}
#[test]
fn test_language_registry_detect() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
let adapter = registry.detect_language(Path::new("test.mock"));
assert!(adapter.is_some());
assert_eq!(adapter.unwrap().name(), "mock");
}
#[test]
fn test_language_registry_detect_unknown() {
let registry = LanguageRegistry::new();
let adapter = registry.detect_language(Path::new("test.unknown"));
assert!(adapter.is_none());
}
#[test]
fn test_language_registry_get_adapter() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
let adapter = registry.get_adapter("mock");
assert!(adapter.is_some());
assert_eq!(adapter.unwrap().name(), "mock");
}
#[test]
fn test_language_registry_get_adapter_unknown() {
let registry = LanguageRegistry::new();
let adapter = registry.get_adapter("unknown");
assert!(adapter.is_none(), "Unknown adapter should return None");
}
#[test]
fn test_language_registry_languages_empty() {
let registry = LanguageRegistry::new();
assert_eq!(
registry.languages().len(),
0,
"Empty registry should have no languages"
);
}
#[test]
fn test_language_registry_languages_multiple() {
struct MockAdapter2;
#[async_trait]
impl LanguageAdapter for MockAdapter2 {
fn name(&self) -> &str {
"mock2"
}
fn extensions(&self) -> &[&str] {
&["m2"]
}
async fn parse(&self, source: &str) -> Result<String> {
Ok(source.to_string())
}
async fn unparse(&self, ast: &str) -> Result<String> {
Ok(ast.to_string())
}
fn mutation_operators(&self) -> Vec<Box<dyn MutationOperator>> {
vec![]
}
async fn run_tests(&self, _source_file: &Path) -> Result<TestRunResult> {
Ok(TestRunResult {
passed: true,
failures: vec![],
execution_time_ms: 100,
stdout: String::new(),
stderr: String::new(),
})
}
}
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
registry.register(Arc::new(MockAdapter2));
let languages = registry.languages();
assert_eq!(languages.len(), 2);
assert!(languages.contains(&"mock"));
assert!(languages.contains(&"mock2"));
}
#[test]
fn test_language_registry_default() {
let registry = LanguageRegistry::default();
assert_eq!(
registry.languages().len(),
0,
"Default registry should be empty"
);
}
#[test]
fn test_language_registry_detect_no_extension() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
let adapter = registry.detect_language(Path::new("noextension"));
assert!(adapter.is_none(), "File without extension should not match");
}
#[test]
fn test_language_registry_detect_case_sensitive() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
let adapter = registry.detect_language(Path::new("test.MOCK"));
assert!(
adapter.is_none(),
"Extension matching should be case-sensitive"
);
}
#[test]
fn test_test_run_result_construction() {
let result = TestRunResult {
passed: true,
failures: vec!["test1".to_string()],
execution_time_ms: 250,
stdout: "output".to_string(),
stderr: "errors".to_string(),
};
assert!(result.passed);
assert_eq!(result.failures.len(), 1);
assert_eq!(result.execution_time_ms, 250);
assert_eq!(result.stdout, "output");
assert_eq!(result.stderr, "errors");
}
#[test]
fn test_language_enum_integration_rust() {
use crate::services::mutation::RustAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(RustAdapter::new()));
let adapter = registry.detect_language(Path::new("test.rs"));
assert!(adapter.is_some(), "Rust file should be detected");
assert_eq!(adapter.unwrap().name(), "rust");
}
#[test]
fn test_language_enum_integration_python() {
use crate::services::mutation::PythonAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(PythonAdapter::new()));
let adapter = registry.detect_language(Path::new("test.py"));
assert!(adapter.is_some(), "Python file should be detected");
assert_eq!(adapter.unwrap().name(), "python");
}
#[test]
fn test_language_enum_integration_typescript() {
use crate::services::mutation::TypeScriptAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(TypeScriptAdapter::new()));
let adapter = registry.detect_language(Path::new("test.ts"));
assert!(adapter.is_some(), "TypeScript file should be detected");
assert_eq!(adapter.unwrap().name(), "typescript");
let adapter_tsx = registry.detect_language(Path::new("component.tsx"));
assert!(adapter_tsx.is_some(), "TSX file should be detected");
assert_eq!(adapter_tsx.unwrap().name(), "typescript");
}
#[test]
fn test_language_enum_integration_javascript() {
use crate::services::mutation::TypeScriptAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(TypeScriptAdapter::new()));
let adapter = registry.detect_language(Path::new("test.js"));
assert!(adapter.is_some(), "JavaScript file should be detected");
assert_eq!(adapter.unwrap().name(), "typescript");
let adapter_jsx = registry.detect_language(Path::new("component.jsx"));
assert!(adapter_jsx.is_some(), "JSX file should be detected");
assert_eq!(adapter_jsx.unwrap().name(), "typescript");
}
#[test]
fn test_language_enum_integration_go() {
use crate::services::mutation::GoAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(GoAdapter::new()));
let adapter = registry.detect_language(Path::new("main.go"));
assert!(adapter.is_some(), "Go file should be detected");
assert_eq!(adapter.unwrap().name(), "go");
}
#[test]
fn test_language_enum_integration_cpp() {
use crate::services::mutation::CppAdapter;
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(CppAdapter::new()));
let extensions = vec!["cpp", "cc", "cxx", "hpp", "hxx", "h"];
for ext in extensions {
let filename = format!("test.{}", ext);
let adapter = registry.detect_language(Path::new(&filename));
assert!(adapter.is_some(), "{} file should be detected", ext);
assert_eq!(adapter.unwrap().name(), "cpp");
}
}
#[test]
fn test_language_enum_integration_unsupported() {
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(MockAdapter));
let adapter = registry.detect_language(Path::new("test.xyz"));
assert!(
adapter.is_none(),
"Unsupported extension should return None"
);
}
#[test]
fn test_language_enum_integration_multi_language() {
use crate::services::mutation::{PythonAdapter, RustAdapter, TypeScriptAdapter};
let mut registry = LanguageRegistry::new();
registry.register(Arc::new(RustAdapter::new()));
registry.register(Arc::new(PythonAdapter::new()));
registry.register(Arc::new(TypeScriptAdapter::new()));
assert!(registry.detect_language(Path::new("test.rs")).is_some());
assert!(registry.detect_language(Path::new("test.py")).is_some());
assert!(registry.detect_language(Path::new("test.ts")).is_some());
assert!(registry.detect_language(Path::new("test.js")).is_some());
let languages = registry.languages();
assert_eq!(languages.len(), 3);
assert!(languages.contains(&"rust"));
assert!(languages.contains(&"python"));
assert!(languages.contains(&"typescript"));
}
}