Skip to main content

ryo_executor/executor/registry/converters/
method.rs

1//! MethodConverter: Converts MutationSpec::AddMethod and RemoveMethod
2
3use crate::engine::ASTRegApply;
4use crate::executor::registry::converters::ResolveTargetSymbol;
5use crate::executor::registry::{ConvertError, MutationConverter};
6use crate::executor::spec::{MutationSpec, SelfParam};
7use ryo_analysis::AnalysisContext;
8use ryo_mutations::{AddMethodMutation, RemoveMethodMutation};
9
10/// Converter for Method mutations (AddMethod, RemoveMethod)
11#[derive(Debug, Clone, Default)]
12pub struct MethodConverter;
13
14impl MethodConverter {
15    pub fn new() -> Self {
16        Self
17    }
18}
19
20// MethodConverter uses the default implementation of ResolveTargetSymbol
21impl ResolveTargetSymbol for MethodConverter {}
22
23impl MutationConverter for MethodConverter {
24    fn spec_kinds(&self) -> &'static [&'static str] {
25        &["AddMethod", "RemoveMethod"]
26    }
27
28    fn convert_v2(
29        &self,
30        spec: &MutationSpec,
31        ctx: &AnalysisContext,
32    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
33        match spec {
34            MutationSpec::AddMethod {
35                target: target_symbol,
36                method_name,
37                params,
38                return_type,
39                body,
40                is_pub,
41                self_param,
42            } => {
43                use ryo_symbol::SymbolKind;
44
45                // New design: Resolve target_symbol to Struct/Enum ID directly
46                // No impl block search needed
47                let type_id = self.resolve_target_symbol(target_symbol, ctx)?;
48
49                // Verify it's a Struct or Enum
50                let kind = ctx.registry.kind(type_id);
51                if !matches!(kind, Some(SymbolKind::Struct | SymbolKind::Enum)) {
52                    return Err(ConvertError::TargetNotFound(format!(
53                        "Expected struct or enum, got {:?}",
54                        kind
55                    )));
56                }
57
58                // Build AddMethodMutation
59                let mut mutation = AddMethodMutation::new(type_id, method_name);
60
61                // Apply self parameter
62                mutation = match self_param {
63                    Some(SelfParam::Ref) => mutation.with_self(),
64                    Some(SelfParam::Mut) => mutation.with_mut_self(),
65                    Some(SelfParam::Owned) => mutation.with_owned_self(),
66                    None => mutation,
67                };
68
69                // Apply params
70                if !params.is_empty() {
71                    mutation = mutation.with_params(params.clone());
72                }
73
74                // Apply return type
75                if let Some(ret) = return_type {
76                    mutation = mutation.with_return_type(ret);
77                }
78
79                // Apply body
80                mutation = mutation.with_body(body);
81
82                // Apply visibility
83                if *is_pub {
84                    mutation = mutation.public();
85                }
86
87                Ok(vec![Box::new(mutation)])
88            }
89            MutationSpec::RemoveMethod {
90                target: target_symbol,
91                method_name,
92            } => {
93                use ryo_symbol::SymbolKind;
94
95                // New design: Resolve to Type ID, then find Type::method
96                let type_id = self.resolve_target_symbol(target_symbol, ctx)?;
97
98                // Verify it's a Struct or Enum
99                let kind = ctx.registry.kind(type_id);
100                if !matches!(kind, Some(SymbolKind::Struct | SymbolKind::Enum)) {
101                    return Err(ConvertError::TargetNotFound(format!(
102                        "Expected struct or enum, got {:?}",
103                        kind
104                    )));
105                }
106
107                // Get type path
108                let type_path = ctx.registry.resolve(type_id).ok_or_else(|| {
109                    ConvertError::TargetNotFound(format!("Type {} not found", type_id))
110                })?;
111
112                // Build method path: Type::method
113                let method_path = type_path.child(method_name).map_err(|_| {
114                    ConvertError::TargetNotFound(format!("Invalid method name: {}", method_name))
115                })?;
116
117                // Find method ID
118                let method_id = ctx.registry.lookup(&method_path).ok_or_else(|| {
119                    ConvertError::TargetNotFound(format!(
120                        "Method '{}' not found on type",
121                        method_name
122                    ))
123                })?;
124
125                let mutation = RemoveMethodMutation::new(method_id);
126                Ok(vec![Box::new(mutation)])
127            }
128            _ => Err(ConvertError::TypeMismatch {
129                expected: "AddMethod or RemoveMethod",
130                actual: spec.kind_name().to_string(),
131            }),
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_method_converter_spec_kinds() {
142        let converter = MethodConverter::new();
143        assert_eq!(converter.spec_kinds(), &["AddMethod", "RemoveMethod"]);
144    }
145
146    #[test]
147    fn test_method_converter_can_handle() {
148        let converter = MethodConverter::new();
149
150        let spec = MutationSpec::AddMethod {
151            target: crate::executor::spec::MutationTargetSymbol::ByKindAndName(
152                crate::executor::ItemKind::Impl,
153                "Config".to_string(),
154            ),
155            method_name: "new".into(),
156            params: vec![],
157            return_type: Some("Self".into()),
158            body: "Self { }".into(),
159            is_pub: true,
160            self_param: None,
161        };
162        assert!(converter.can_handle(&spec));
163    }
164}