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};
#[derive(Debug, Clone, Default)]
pub struct MethodConverter;
impl MethodConverter {
pub fn new() -> Self {
Self
}
}
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;
let type_id = self.resolve_target_symbol(target_symbol, ctx)?;
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
)));
}
let mut mutation = AddMethodMutation::new(type_id, method_name);
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,
};
if !params.is_empty() {
mutation = mutation.with_params(params.clone());
}
if let Some(ret) = return_type {
mutation = mutation.with_return_type(ret);
}
mutation = mutation.with_body(body);
if *is_pub {
mutation = mutation.public();
}
Ok(vec![Box::new(mutation)])
}
MutationSpec::RemoveMethod {
target: target_symbol,
method_name,
} => {
use ryo_symbol::SymbolKind;
let type_id = self.resolve_target_symbol(target_symbol, ctx)?;
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
)));
}
let type_path = ctx.registry.resolve(type_id).ok_or_else(|| {
ConvertError::TargetNotFound(format!("Type {} not found", type_id))
})?;
let method_path = type_path.child(method_name).map_err(|_| {
ConvertError::TargetNotFound(format!("Invalid method name: {}", method_name))
})?;
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));
}
}