use super::isolator::ReproCase;
#[derive(Debug, Clone)]
pub struct Fix {
pub id: String,
pub pattern_id: String,
pub description: String,
pub rust_output: String,
pub confidence: f64,
pub transformation: String,
}
impl Fix {
#[must_use]
pub fn new(id: impl Into<String>, pattern_id: impl Into<String>) -> Self {
Self {
id: id.into(),
pattern_id: pattern_id.into(),
description: String::new(),
rust_output: String::new(),
confidence: 0.0,
transformation: String::new(),
}
}
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = desc.into();
self
}
pub fn with_rust_output(mut self, output: impl Into<String>) -> Self {
self.rust_output = output.into();
self
}
pub fn with_confidence(mut self, confidence: f64) -> Self {
self.confidence = confidence;
self
}
pub fn with_transformation(mut self, transform: impl Into<String>) -> Self {
self.transformation = transform.into();
self
}
#[must_use]
pub fn is_high_confidence(&self, threshold: f64) -> bool {
self.confidence >= threshold
}
}
#[derive(Debug, Clone)]
pub enum RepairResult {
Success(Fix),
NeedsHumanReview {
fix: Fix,
confidence: f64,
reason: String,
},
NoFixFound,
}
pub trait Mutator: std::fmt::Debug {
fn name(&self) -> &str;
fn applies_to(&self, error_code: &str) -> bool;
fn apply(&self, source: &str, error_code: &str) -> Option<String>;
fn confidence(&self) -> f64;
}
#[derive(Debug)]
pub struct TypeCoercionMutator;
impl Mutator for TypeCoercionMutator {
fn name(&self) -> &'static str {
"type_coercion"
}
fn applies_to(&self, error_code: &str) -> bool {
error_code == "E0308"
}
fn apply(&self, source: &str, _error_code: &str) -> Option<String> {
if source.contains('"') && source.contains("-> i32") {
Some(source.replace('"', "").replace("-> i32", "-> String"))
} else if source.contains('"') && source.contains("-> String") {
Some(source.replace("-> String {", "-> String { return "))
} else {
None
}
}
fn confidence(&self) -> f64 {
0.75
}
}
#[derive(Debug)]
pub struct MethodAdditionMutator;
impl Mutator for MethodAdditionMutator {
fn name(&self) -> &'static str {
"method_addition"
}
fn applies_to(&self, error_code: &str) -> bool {
error_code == "E0599"
}
fn apply(&self, source: &str, _error_code: &str) -> Option<String> {
if source.contains(".nonexistent_method()") {
Some(source.replace(".nonexistent_method()", ""))
} else {
None
}
}
fn confidence(&self) -> f64 {
0.60
}
}
#[derive(Debug)]
pub struct ImportAdditionMutator;
impl Mutator for ImportAdditionMutator {
fn name(&self) -> &'static str {
"import_addition"
}
fn applies_to(&self, error_code: &str) -> bool {
error_code == "E0432" || error_code == "E0433"
}
fn apply(&self, source: &str, _error_code: &str) -> Option<String> {
if source.contains("use nonexistent") {
Some(source.replace("use nonexistent", "// use nonexistent"))
} else {
None
}
}
fn confidence(&self) -> f64 {
0.50
}
}
#[derive(Debug)]
pub struct JidokaRepairEngine {
mutators: Vec<Box<dyn Mutator + Send + Sync>>,
quality_threshold: f64,
human_review_threshold: f64,
}
impl Default for JidokaRepairEngine {
fn default() -> Self {
Self::new()
}
}
impl JidokaRepairEngine {
#[must_use]
pub fn new() -> Self {
let mutators: Vec<Box<dyn Mutator + Send + Sync>> = vec![
Box::new(TypeCoercionMutator),
Box::new(MethodAdditionMutator),
Box::new(ImportAdditionMutator),
];
Self {
mutators,
quality_threshold: 0.85,
human_review_threshold: 0.70,
}
}
#[must_use]
pub fn with_quality_threshold(mut self, threshold: f64) -> Self {
self.quality_threshold = threshold;
self
}
#[must_use]
pub fn with_human_review_threshold(mut self, threshold: f64) -> Self {
self.human_review_threshold = threshold;
self
}
pub fn add_mutator(&mut self, mutator: Box<dyn Mutator + Send + Sync>) {
self.mutators.push(mutator);
}
#[must_use]
pub fn attempt_repair(&self, repro: &ReproCase) -> RepairResult {
let error_code = &repro.expected_error;
for mutator in &self.mutators {
if !mutator.applies_to(error_code) {
continue;
}
if let Some(fixed_code) = mutator.apply(&repro.source, error_code) {
let confidence = mutator.confidence();
let fix = Fix::new(
format!("FIX-{}-{}", error_code, mutator.name()),
&repro.pattern_id,
)
.with_description(format!("Applied {} mutator", mutator.name()))
.with_rust_output(fixed_code)
.with_confidence(confidence)
.with_transformation(mutator.name().to_string());
if confidence < self.human_review_threshold {
return RepairResult::NeedsHumanReview {
fix,
confidence,
reason: "Low confidence - manual review required".to_string(),
};
}
if confidence >= self.quality_threshold {
return RepairResult::Success(fix);
}
return RepairResult::NeedsHumanReview {
fix,
confidence,
reason: "Moderate confidence - recommend review".to_string(),
};
}
}
RepairResult::NoFixFound
}
#[must_use]
pub fn mutators(&self) -> &[Box<dyn Mutator + Send + Sync>] {
&self.mutators
}
#[must_use]
pub fn mutator_count(&self) -> usize {
self.mutators.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fix_new() {
let fix = Fix::new("FIX-001", "PAT-001");
assert_eq!(fix.id, "FIX-001");
assert_eq!(fix.pattern_id, "PAT-001");
}
#[test]
fn test_fix_with_description() {
let fix = Fix::new("FIX-001", "PAT-001").with_description("Test fix");
assert_eq!(fix.description, "Test fix");
}
#[test]
fn test_fix_with_rust_output() {
let fix = Fix::new("FIX-001", "PAT-001").with_rust_output("fn test() {}");
assert_eq!(fix.rust_output, "fn test() {}");
}
#[test]
fn test_fix_with_confidence() {
let fix = Fix::new("FIX-001", "PAT-001").with_confidence(0.95);
assert!((fix.confidence - 0.95).abs() < f64::EPSILON);
}
#[test]
fn test_fix_with_transformation() {
let fix = Fix::new("FIX-001", "PAT-001").with_transformation("type_coercion");
assert_eq!(fix.transformation, "type_coercion");
}
#[test]
fn test_fix_is_high_confidence() {
let fix = Fix::new("FIX-001", "PAT-001").with_confidence(0.9);
assert!(fix.is_high_confidence(0.85));
assert!(!fix.is_high_confidence(0.95));
}
#[test]
fn test_repair_result_success() {
let fix = Fix::new("FIX-001", "PAT-001");
let result = RepairResult::Success(fix);
assert!(matches!(result, RepairResult::Success(_)));
}
#[test]
fn test_repair_result_needs_review() {
let fix = Fix::new("FIX-001", "PAT-001");
let result = RepairResult::NeedsHumanReview {
fix,
confidence: 0.6,
reason: "Low confidence".to_string(),
};
assert!(matches!(result, RepairResult::NeedsHumanReview { .. }));
}
#[test]
fn test_repair_result_no_fix() {
let result = RepairResult::NoFixFound;
assert!(matches!(result, RepairResult::NoFixFound));
}
#[test]
fn test_type_coercion_mutator_name() {
let mutator = TypeCoercionMutator;
assert_eq!(mutator.name(), "type_coercion");
}
#[test]
fn test_type_coercion_mutator_applies_to() {
let mutator = TypeCoercionMutator;
assert!(mutator.applies_to("E0308"));
assert!(!mutator.applies_to("E0599"));
}
#[test]
fn test_type_coercion_mutator_confidence() {
let mutator = TypeCoercionMutator;
assert!((mutator.confidence() - 0.75).abs() < f64::EPSILON);
}
#[test]
fn test_method_addition_mutator_name() {
let mutator = MethodAdditionMutator;
assert_eq!(mutator.name(), "method_addition");
}
#[test]
fn test_method_addition_mutator_applies_to() {
let mutator = MethodAdditionMutator;
assert!(mutator.applies_to("E0599"));
assert!(!mutator.applies_to("E0308"));
}
#[test]
fn test_method_addition_mutator_apply() {
let mutator = MethodAdditionMutator;
let result = mutator.apply("x.nonexistent_method()", "E0599");
assert!(result.is_some());
assert_eq!(result.unwrap(), "x");
}
#[test]
fn test_import_addition_mutator_name() {
let mutator = ImportAdditionMutator;
assert_eq!(mutator.name(), "import_addition");
}
#[test]
fn test_import_addition_mutator_applies_to() {
let mutator = ImportAdditionMutator;
assert!(mutator.applies_to("E0432"));
assert!(mutator.applies_to("E0433"));
assert!(!mutator.applies_to("E0308"));
}
#[test]
fn test_import_addition_mutator_apply() {
let mutator = ImportAdditionMutator;
let result = mutator.apply("use nonexistent_crate::Thing;", "E0432");
assert!(result.is_some());
assert!(result.unwrap().contains("//"));
}
#[test]
fn test_jidoka_repair_engine_new() {
let engine = JidokaRepairEngine::new();
assert_eq!(engine.mutator_count(), 3);
}
#[test]
fn test_jidoka_repair_engine_default() {
let engine = JidokaRepairEngine::default();
assert_eq!(engine.mutator_count(), 3);
}
#[test]
fn test_jidoka_repair_engine_with_quality_threshold() {
let engine = JidokaRepairEngine::new().with_quality_threshold(0.90);
assert!((engine.quality_threshold - 0.90).abs() < f64::EPSILON);
}
#[test]
fn test_jidoka_repair_engine_with_human_review_threshold() {
let engine = JidokaRepairEngine::new().with_human_review_threshold(0.60);
assert!((engine.human_review_threshold - 0.60).abs() < f64::EPSILON);
}
#[test]
fn test_jidoka_repair_engine_attempt_repair_e0599() {
let engine = JidokaRepairEngine::new();
let repro = ReproCase::new("x.nonexistent_method()", "E0599");
let result = engine.attempt_repair(&repro);
assert!(matches!(result, RepairResult::NeedsHumanReview { .. }));
}
#[test]
fn test_jidoka_repair_engine_attempt_repair_unknown() {
let engine = JidokaRepairEngine::new();
let repro = ReproCase::new("fn test() {}", "E9999");
let result = engine.attempt_repair(&repro);
assert!(matches!(result, RepairResult::NoFixFound));
}
#[test]
fn test_jidoka_repair_engine_mutators() {
let engine = JidokaRepairEngine::new();
let mutators = engine.mutators();
assert_eq!(mutators.len(), 3);
}
}