Skip to main content

ryo_executor/executor/registry/converters/
duplicate.rs

1//! DuplicateConverter: Converts Duplicate MutationSpecs to AddPureItemsMutation
2
3use crate::engine::ASTRegApply;
4use crate::executor::registry::converters::ResolveTargetSymbol;
5use crate::executor::registry::{ConvertError, MutationConverter};
6use crate::executor::spec::MutationSpec;
7use ryo_analysis::AnalysisContext;
8use ryo_mutations::AddPureItemsMutation;
9use ryo_source::pure::PureItem;
10use ryo_symbol::SymbolKind;
11
12/// Converter for Duplicate mutations
13#[derive(Debug, Clone, Default)]
14pub struct DuplicateConverter;
15
16impl DuplicateConverter {
17    pub fn new() -> Self {
18        Self
19    }
20}
21
22// DuplicateConverter uses the default implementation of ResolveTargetSymbol
23impl ResolveTargetSymbol for DuplicateConverter {}
24
25impl MutationConverter for DuplicateConverter {
26    fn spec_kinds(&self) -> &'static [&'static str] {
27        &[
28            "DuplicateFunction",
29            "DuplicateStruct",
30            "DuplicateEnum",
31            "DuplicateModTree",
32        ]
33    }
34
35    fn convert_v2(
36        &self,
37        spec: &MutationSpec,
38        ctx: &AnalysisContext,
39    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
40        match spec {
41            MutationSpec::DuplicateFunction {
42                target: target_symbol,
43                to,
44            } => {
45                // Resolve target_symbol to SymbolId
46                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
47
48                // Get source AST
49                let source_ast = ctx
50                    .ast_registry
51                    .get(symbol_id)
52                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
53
54                let mut new_fn = match source_ast {
55                    PureItem::Fn(f) => f.clone(),
56                    _ => {
57                        return Err(ConvertError::Apply(format!(
58                            "Symbol {:?} is not a function",
59                            symbol_id
60                        )))
61                    }
62                };
63
64                // Rename
65                new_fn.name = to.clone();
66
67                // Get parent module
68                let source_path = ctx
69                    .registry
70                    .path(symbol_id)
71                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
72                let parent_path = source_path.parent().ok_or_else(|| {
73                    ConvertError::Apply("Source function has no parent module".to_string())
74                })?;
75                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
76                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
77                })?;
78
79                Ok(vec![Box::new(AddPureItemsMutation::new(
80                    parent_id,
81                    vec![PureItem::Fn(new_fn)],
82                ))])
83            }
84            MutationSpec::DuplicateStruct {
85                target: target_symbol,
86                to,
87                include_impls,
88            } => {
89                // Resolve target_symbol to SymbolId
90                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
91
92                // Get source AST
93                let source_ast = ctx
94                    .ast_registry
95                    .get(symbol_id)
96                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
97
98                let (mut new_struct, source_name) = match source_ast {
99                    PureItem::Struct(s) => (s.clone(), s.name.clone()),
100                    _ => {
101                        return Err(ConvertError::Apply(format!(
102                            "Symbol {:?} is not a struct",
103                            symbol_id
104                        )))
105                    }
106                };
107
108                // Rename
109                new_struct.name = to.clone();
110
111                let mut items = vec![PureItem::Struct(new_struct)];
112
113                // Duplicate impl blocks if requested
114                if *include_impls {
115                    for (impl_id, _) in ctx.registry.iter() {
116                        if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
117                            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
118                                if imp.self_ty == source_name {
119                                    let mut new_impl = imp.clone();
120                                    new_impl.self_ty = to.clone();
121                                    items.push(PureItem::Impl(new_impl));
122                                }
123                            }
124                        }
125                    }
126                }
127
128                // Get parent module
129                let source_path = ctx
130                    .registry
131                    .path(symbol_id)
132                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
133                let parent_path = source_path.parent().ok_or_else(|| {
134                    ConvertError::Apply("Source struct has no parent module".to_string())
135                })?;
136                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
137                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
138                })?;
139
140                Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
141            }
142            MutationSpec::DuplicateEnum {
143                target: target_symbol,
144                to,
145                include_impls,
146            } => {
147                // Resolve target_symbol to SymbolId
148                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
149
150                // Get source AST
151                let source_ast = ctx
152                    .ast_registry
153                    .get(symbol_id)
154                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
155
156                let (mut new_enum, source_name) = match source_ast {
157                    PureItem::Enum(e) => (e.clone(), e.name.clone()),
158                    _ => {
159                        return Err(ConvertError::Apply(format!(
160                            "Symbol {:?} is not an enum",
161                            symbol_id
162                        )))
163                    }
164                };
165
166                // Rename
167                new_enum.name = to.clone();
168
169                let mut items = vec![PureItem::Enum(new_enum)];
170
171                // Duplicate impl blocks if requested
172                if *include_impls {
173                    for (impl_id, _) in ctx.registry.iter() {
174                        if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
175                            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
176                                if imp.self_ty == source_name {
177                                    let mut new_impl = imp.clone();
178                                    new_impl.self_ty = to.clone();
179                                    items.push(PureItem::Impl(new_impl));
180                                }
181                            }
182                        }
183                    }
184                }
185
186                // Get parent module
187                let source_path = ctx
188                    .registry
189                    .path(symbol_id)
190                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
191                let parent_path = source_path.parent().ok_or_else(|| {
192                    ConvertError::Apply("Source enum has no parent module".to_string())
193                })?;
194                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
195                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
196                })?;
197
198                Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
199            }
200            MutationSpec::DuplicateModTree {
201                target: target_symbol,
202                to,
203            } => {
204                // Resolve target_symbol to SymbolId
205                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
206
207                // Get source AST
208                let source_ast = ctx
209                    .ast_registry
210                    .get(symbol_id)
211                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
212
213                let mut new_mod = match source_ast {
214                    PureItem::Mod(m) => m.clone(),
215                    _ => {
216                        return Err(ConvertError::Apply(format!(
217                            "Symbol {:?} is not a module",
218                            symbol_id
219                        )))
220                    }
221                };
222
223                // Rename
224                new_mod.name = to.clone();
225
226                // Get parent module
227                let source_path = ctx
228                    .registry
229                    .path(symbol_id)
230                    .ok_or(ConvertError::SymbolNotFound(symbol_id))?;
231                let parent_path = source_path.parent().ok_or_else(|| {
232                    ConvertError::Apply("Source module has no parent module".to_string())
233                })?;
234                let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
235                    ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
236                })?;
237
238                Ok(vec![Box::new(AddPureItemsMutation::new(
239                    parent_id,
240                    vec![PureItem::Mod(new_mod)],
241                ))])
242            }
243            _ => Err(ConvertError::TypeMismatch {
244                expected: "Duplicate*",
245                actual: spec.kind_name().to_string(),
246            }),
247        }
248    }
249}