ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! MethodConverter: Converts MutationSpec::AddMethod and RemoveMethod

use crate::engine::ASTRegApply;
use crate::executor::registry::converters::ResolveTargetSymbol;
use crate::executor::registry::{ConvertError, MutationConverter};
use crate::executor::spec::{MutationSpec, SelfParam};
use ryo_analysis::AnalysisContext;
use ryo_mutations::{AddMethodMutation, RemoveMethodMutation};

/// Converter for Method mutations (AddMethod, RemoveMethod)
#[derive(Debug, Clone, Default)]
pub struct MethodConverter;

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

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

impl MutationConverter for MethodConverter {
    fn spec_kinds(&self) -> &'static [&'static str] {
        &["AddMethod", "RemoveMethod"]
    }

    fn convert_v2(
        &self,
        spec: &MutationSpec,
        ctx: &AnalysisContext,
    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
        match spec {
            MutationSpec::AddMethod {
                target: target_symbol,
                method_name,
                params,
                return_type,
                body,
                is_pub,
                self_param,
            } => {
                use ryo_symbol::SymbolKind;

                // New design: Resolve target_symbol to Struct/Enum ID directly
                // No impl block search needed
                let type_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Verify it's a Struct or Enum
                let kind = ctx.registry.kind(type_id);
                if !matches!(kind, Some(SymbolKind::Struct | SymbolKind::Enum)) {
                    return Err(ConvertError::TargetNotFound(format!(
                        "Expected struct or enum, got {:?}",
                        kind
                    )));
                }

                // Build AddMethodMutation
                let mut mutation = AddMethodMutation::new(type_id, method_name);

                // Apply self parameter
                mutation = match self_param {
                    Some(SelfParam::Ref) => mutation.with_self(),
                    Some(SelfParam::Mut) => mutation.with_mut_self(),
                    Some(SelfParam::Owned) => mutation.with_owned_self(),
                    None => mutation,
                };

                // Apply params
                if !params.is_empty() {
                    mutation = mutation.with_params(params.clone());
                }

                // Apply return type
                if let Some(ret) = return_type {
                    mutation = mutation.with_return_type(ret);
                }

                // Apply body
                mutation = mutation.with_body(body);

                // Apply visibility
                if *is_pub {
                    mutation = mutation.public();
                }

                Ok(vec![Box::new(mutation)])
            }
            MutationSpec::RemoveMethod {
                target: target_symbol,
                method_name,
            } => {
                use ryo_symbol::SymbolKind;

                // New design: Resolve to Type ID, then find Type::method
                let type_id = self.resolve_target_symbol(target_symbol, ctx)?;

                // Verify it's a Struct or Enum
                let kind = ctx.registry.kind(type_id);
                if !matches!(kind, Some(SymbolKind::Struct | SymbolKind::Enum)) {
                    return Err(ConvertError::TargetNotFound(format!(
                        "Expected struct or enum, got {:?}",
                        kind
                    )));
                }

                // Get type path
                let type_path = ctx.registry.resolve(type_id).ok_or_else(|| {
                    ConvertError::TargetNotFound(format!("Type {} not found", type_id))
                })?;

                // Build method path: Type::method
                let method_path = type_path.child(method_name).map_err(|_| {
                    ConvertError::TargetNotFound(format!("Invalid method name: {}", method_name))
                })?;

                // Find method ID
                let method_id = ctx.registry.lookup(&method_path).ok_or_else(|| {
                    ConvertError::TargetNotFound(format!(
                        "Method '{}' not found on type",
                        method_name
                    ))
                })?;

                let mutation = RemoveMethodMutation::new(method_id);
                Ok(vec![Box::new(mutation)])
            }
            _ => Err(ConvertError::TypeMismatch {
                expected: "AddMethod or RemoveMethod",
                actual: spec.kind_name().to_string(),
            }),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_method_converter_spec_kinds() {
        let converter = MethodConverter::new();
        assert_eq!(converter.spec_kinds(), &["AddMethod", "RemoveMethod"]);
    }

    #[test]
    fn test_method_converter_can_handle() {
        let converter = MethodConverter::new();

        let spec = MutationSpec::AddMethod {
            target: crate::executor::spec::MutationTargetSymbol::ByKindAndName(
                crate::executor::ItemKind::Impl,
                "Config".to_string(),
            ),
            method_name: "new".into(),
            params: vec![],
            return_type: Some("Self".into()),
            body: "Self { }".into(),
            is_pub: true,
            self_param: None,
        };
        assert!(converter.can_handle(&spec));
    }
}