ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! Individual MutationConverter implementations
//!
//! Each module handles one or more MutationSpec variants.
//!
//! # Implementation Status
//!
//! ## Phase 1: Basic Converters
//! - [x] rename.rs - RenameConverter
//! - [x] field.rs - FieldConverter (AddField, RemoveField)
//! - [x] visibility.rs - VisibilityConverter
//! - [x] derive.rs - DeriveConverter (AddDerive, RemoveDerive)
//!
//! ## Phase 2: Complex Converters
//! - [x] enum_.rs - EnumConverter (AddVariant, RemoveVariant)
//! - [x] remove.rs - RemoveConverter (RemoveItem)
//! - [x] method.rs - MethodConverter
//! - [x] module.rs - ModuleConverter (AddMod, RemoveMod, CreateMod)
//! - [x] add_item/ - AddItemConverter (with ItemParser sub-parsers)
//!
//! ## Phase 3: Remaining Converters
//! - [x] idiom.rs - IdiomConverter (OrganizeImports, LoopToIterator, etc.)
//! - [x] trait_.rs - TraitConverter (ExtractTrait, InlineTrait)
//! - [x] move_.rs - MoveConverter
//! - [x] plugin.rs - PluginConverter (WASM skeleton)
//! - [x] stmt.rs - StmtConverter (ReplaceExpr, RemoveStatement, InsertStatement, ReplaceStatement)
//! - [x] match_arm.rs - MatchArmConverter (AddMatchArm)
//! - [x] struct_literal_field.rs - StructLiteralFieldConverter (AddStructLiteralField)
//! - [x] duplicate.rs - DuplicateConverter (DuplicateFunction, DuplicateStruct, DuplicateEnum, DuplicateModTree)

use crate::executor::registry::ConvertError;
use crate::executor::spec::MutationTargetSymbol;
use ryo_analysis::AnalysisContext;
use ryo_symbol::SymbolId;

/// Trait for resolving MutationTargetSymbol to SymbolId.
///
/// This trait provides a unified interface for all converters to resolve
/// lazy symbol targeting. The default implementation covers all resolution
/// strategies (ById, ByPath, ByKindAndName, ByAffectedId).
pub trait ResolveTargetSymbol {
    /// Resolve MutationTargetSymbol to SymbolId.
    ///
    /// Called during Wave execution (DetectConflict phase).
    fn resolve_target_symbol(
        &self,
        target: &MutationTargetSymbol,
        ctx: &AnalysisContext,
    ) -> Result<SymbolId, ConvertError> {
        match target {
            // Already resolved
            MutationTargetSymbol::ById(id) => {
                // Verify it exists in registry
                if ctx.registry.resolve(*id).is_none() {
                    return Err(ConvertError::TargetNotFound(format!(
                        "SymbolId {:?} not found in registry",
                        id
                    )));
                }
                Ok(*id)
            }

            // Lazy resolution by path
            MutationTargetSymbol::ByPath(path) => {
                // Lookup symbol by path
                ctx.registry
                    .iter()
                    .find(|(_, p)| *p == &**path)
                    .map(|(id, _)| id)
                    .ok_or_else(|| {
                        ConvertError::TargetNotFound(format!("Symbol not found by path: {}", path))
                    })
            }

            // Lazy resolution by kind + name
            MutationTargetSymbol::ByKindAndName(kind, name) => {
                // Search for symbol matching kind and name
                // For impl blocks with generics, normalize both names before comparison
                // (registry stores "Generic < T , U >" but DSL specifies "Generic<T, U>")
                let normalized_name = normalize_generic_name(name);
                let matches: Vec<_> = ctx
                    .registry
                    .iter()
                    .filter(|(_, path)| normalize_generic_name(path.name()) == normalized_name)
                    .collect();

                match matches.len() {
                    0 => Err(ConvertError::TargetNotFound(format!(
                        "Symbol not found: kind={:?}, name={}",
                        kind, name
                    ))),
                    1 => Ok(matches[0].0),
                    _ => Err(ConvertError::TargetNotFound(format!(
                        "Multiple symbols found: kind={:?}, name={} (found {} matches)",
                        kind,
                        name,
                        matches.len()
                    ))),
                }
            }

            // Derived from parent (e.g., field in newly added struct)
            MutationTargetSymbol::ByAffectedId {
                parent_id,
                kind,
                name,
            } => {
                // For now, we need the parent to exist
                // In future, this could track mutations within the same Wave
                let parent_path = ctx.registry.resolve(*parent_id).ok_or_else(|| {
                    ConvertError::TargetNotFound(format!(
                        "Parent SymbolId {:?} not found",
                        parent_id
                    ))
                })?;

                if let Some(child_name) = name {
                    // Construct child path
                    parent_path
                        .child(child_name)
                        .map_err(|e| {
                            ConvertError::TargetNotFound(format!("Invalid child path: {}", e))
                        })
                        .and_then(|child_path| {
                            // Lookup child in registry
                            ctx.registry
                                .iter()
                                .find(|(_, p)| p == &&child_path)
                                .map(|(id, _)| id)
                                .ok_or_else(|| {
                                    ConvertError::TargetNotFound(format!(
                                        "Child symbol not found: parent={:?}, kind={:?}, name={}",
                                        parent_id, kind, child_name
                                    ))
                                })
                        })
                } else {
                    // Anonymous child - not yet supported
                    Err(ConvertError::TargetNotFound(format!(
                        "Anonymous child symbols not yet supported: parent={:?}, kind={:?}",
                        parent_id, kind
                    )))
                }
            }
        }
    }
}

/// Normalize generic type names for comparison.
///
/// The registry stores generic types with spaces due to `to_token_stream().to_string()`
/// behavior (e.g., "Generic < T , U >"), but DSL specifies them without spaces
/// (e.g., "Generic<T, U>"). This function normalizes both formats for comparison.
fn normalize_generic_name(name: &str) -> String {
    name.replace(" < ", "<")
        .replace(" > ", ">")
        .replace("< ", "<")
        .replace(" >", ">")
        .replace(", ", ",")
        .replace(" ,", ",")
}

// Phase 1 converters
pub mod derive;
pub mod field;
pub mod rename;
pub mod visibility;

// Phase 2 converters
pub mod add_item;
pub mod enum_;
pub mod method;
pub mod module;
pub mod remove;

// Phase 3 converters
pub mod duplicate;
pub mod idiom;
pub mod match_arm;
pub mod move_;
pub mod plugin;
pub mod stmt;
pub mod struct_literal_field;
pub mod trait_;

// Re-exports
pub use add_item::AddItemConverter;
pub use derive::DeriveConverter;
pub use duplicate::DuplicateConverter;
pub use enum_::EnumConverter;
pub use field::FieldConverter;
pub use idiom::IdiomConverter;
pub use match_arm::MatchArmConverter;
pub use method::MethodConverter;
pub use module::ModuleConverter;
pub use move_::MoveConverter;
pub use plugin::PluginConverter;
pub use remove::RemoveConverter;
pub use rename::RenameConverter;
pub use stmt::StmtConverter;
pub use struct_literal_field::StructLiteralFieldConverter;
pub use trait_::TraitConverter;
pub use visibility::VisibilityConverter;