ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! DuplicateConverter: Converts Duplicate MutationSpecs to AddPureItemsMutation

use crate::engine::ASTRegApply;
use crate::executor::registry::converters::ResolveTargetSymbol;
use crate::executor::registry::{ConvertError, MutationConverter};
use crate::executor::spec::MutationSpec;
use ryo_analysis::AnalysisContext;
use ryo_mutations::AddPureItemsMutation;
use ryo_source::pure::PureItem;
use ryo_symbol::SymbolKind;

/// Converter for Duplicate mutations
#[derive(Debug, Clone, Default)]
pub struct DuplicateConverter;

impl DuplicateConverter {
    pub fn new() -> Self {
        Self
    }
}

// DuplicateConverter uses the default implementation of ResolveTargetSymbol
impl ResolveTargetSymbol for DuplicateConverter {}

impl MutationConverter for DuplicateConverter {
    fn spec_kinds(&self) -> &'static [&'static str] {
        &[
            "DuplicateFunction",
            "DuplicateStruct",
            "DuplicateEnum",
            "DuplicateModTree",
        ]
    }

    fn convert_v2(
        &self,
        spec: &MutationSpec,
        ctx: &AnalysisContext,
    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
        match spec {
            MutationSpec::DuplicateFunction {
                target: target_symbol,
                to,
            } => {
                // Resolve target_symbol to SymbolId
                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Get source AST
                let source_ast = ctx
                    .ast_registry
                    .get(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;

                let mut new_fn = match source_ast {
                    PureItem::Fn(f) => f.clone(),
                    _ => {
                        return Err(ConvertError::Apply(format!(
                            "Symbol {:?} is not a function",
                            symbol_id
                        )))
                    }
                };

                // Rename
                new_fn.name = to.clone();

                // Get parent module
                let source_path = ctx
                    .registry
                    .path(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
                let parent_path = source_path.parent().ok_or_else(|| {
                    ConvertError::Apply("Source function has no parent module".to_string())
                })?;
                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
                })?;

                Ok(vec![Box::new(AddPureItemsMutation::new(
                    parent_id,
                    vec![PureItem::Fn(new_fn)],
                ))])
            }
            MutationSpec::DuplicateStruct {
                target: target_symbol,
                to,
                include_impls,
            } => {
                // Resolve target_symbol to SymbolId
                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Get source AST
                let source_ast = ctx
                    .ast_registry
                    .get(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;

                let (mut new_struct, source_name) = match source_ast {
                    PureItem::Struct(s) => (s.clone(), s.name.clone()),
                    _ => {
                        return Err(ConvertError::Apply(format!(
                            "Symbol {:?} is not a struct",
                            symbol_id
                        )))
                    }
                };

                // Rename
                new_struct.name = to.clone();

                let mut items = vec![PureItem::Struct(new_struct)];

                // Duplicate impl blocks if requested
                if *include_impls {
                    for (impl_id, _) in ctx.registry.iter() {
                        if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
                            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
                                if imp.self_ty == source_name {
                                    let mut new_impl = imp.clone();
                                    new_impl.self_ty = to.clone();
                                    items.push(PureItem::Impl(new_impl));
                                }
                            }
                        }
                    }
                }

                // Get parent module
                let source_path = ctx
                    .registry
                    .path(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
                let parent_path = source_path.parent().ok_or_else(|| {
                    ConvertError::Apply("Source struct has no parent module".to_string())
                })?;
                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
                })?;

                Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
            }
            MutationSpec::DuplicateEnum {
                target: target_symbol,
                to,
                include_impls,
            } => {
                // Resolve target_symbol to SymbolId
                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Get source AST
                let source_ast = ctx
                    .ast_registry
                    .get(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;

                let (mut new_enum, source_name) = match source_ast {
                    PureItem::Enum(e) => (e.clone(), e.name.clone()),
                    _ => {
                        return Err(ConvertError::Apply(format!(
                            "Symbol {:?} is not an enum",
                            symbol_id
                        )))
                    }
                };

                // Rename
                new_enum.name = to.clone();

                let mut items = vec![PureItem::Enum(new_enum)];

                // Duplicate impl blocks if requested
                if *include_impls {
                    for (impl_id, _) in ctx.registry.iter() {
                        if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
                            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
                                if imp.self_ty == source_name {
                                    let mut new_impl = imp.clone();
                                    new_impl.self_ty = to.clone();
                                    items.push(PureItem::Impl(new_impl));
                                }
                            }
                        }
                    }
                }

                // Get parent module
                let source_path = ctx
                    .registry
                    .path(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
                let parent_path = source_path.parent().ok_or_else(|| {
                    ConvertError::Apply("Source enum has no parent module".to_string())
                })?;
                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
                })?;

                Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
            }
            MutationSpec::DuplicateModTree {
                target: target_symbol,
                to,
            } => {
                // Resolve target_symbol to SymbolId
                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Get source AST
                let source_ast = ctx
                    .ast_registry
                    .get(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;

                let mut new_mod = match source_ast {
                    PureItem::Mod(m) => m.clone(),
                    _ => {
                        return Err(ConvertError::Apply(format!(
                            "Symbol {:?} is not a module",
                            symbol_id
                        )))
                    }
                };

                // Rename
                new_mod.name = to.clone();

                // Get parent module
                let source_path = ctx
                    .registry
                    .path(symbol_id)
                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
                let parent_path = source_path.parent().ok_or_else(|| {
                    ConvertError::Apply("Source module has no parent module".to_string())
                })?;
                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
                })?;

                Ok(vec![Box::new(AddPureItemsMutation::new(
                    parent_id,
                    vec![PureItem::Mod(new_mod)],
                ))])
            }
            _ => Err(ConvertError::TypeMismatch {
                expected: "Duplicate*",
                actual: spec.kind_name().to_string(),
            }),
        }
    }
}