Skip to main content

ryo_executor/executor/registry/converters/
mod.rs

1//! Individual MutationConverter implementations
2//!
3//! Each module handles one or more MutationSpec variants.
4//!
5//! # Implementation Status
6//!
7//! ## Phase 1: Basic Converters
8//! - [x] rename.rs - RenameConverter
9//! - [x] field.rs - FieldConverter (AddField, RemoveField)
10//! - [x] visibility.rs - VisibilityConverter
11//! - [x] derive.rs - DeriveConverter (AddDerive, RemoveDerive)
12//!
13//! ## Phase 2: Complex Converters
14//! - [x] enum_.rs - EnumConverter (AddVariant, RemoveVariant)
15//! - [x] remove.rs - RemoveConverter (RemoveItem)
16//! - [x] method.rs - MethodConverter
17//! - [x] module.rs - ModuleConverter (AddMod, RemoveMod, CreateMod)
18//! - [x] add_item/ - AddItemConverter (with ItemParser sub-parsers)
19//!
20//! ## Phase 3: Remaining Converters
21//! - [x] idiom.rs - IdiomConverter (OrganizeImports, LoopToIterator, etc.)
22//! - [x] trait_.rs - TraitConverter (ExtractTrait, InlineTrait)
23//! - [x] move_.rs - MoveConverter
24//! - [x] plugin.rs - PluginConverter (WASM skeleton)
25//! - [x] stmt.rs - StmtConverter (ReplaceExpr, RemoveStatement, InsertStatement, ReplaceStatement)
26//! - [x] match_arm.rs - MatchArmConverter (AddMatchArm)
27//! - [x] struct_literal_field.rs - StructLiteralFieldConverter (AddStructLiteralField)
28//! - [x] duplicate.rs - DuplicateConverter (DuplicateFunction, DuplicateStruct, DuplicateEnum, DuplicateModTree)
29
30use crate::executor::registry::ConvertError;
31use crate::executor::spec::MutationTargetSymbol;
32use ryo_analysis::AnalysisContext;
33use ryo_symbol::SymbolId;
34
35/// Trait for resolving MutationTargetSymbol to SymbolId.
36///
37/// This trait provides a unified interface for all converters to resolve
38/// lazy symbol targeting. The default implementation covers all resolution
39/// strategies (ById, ByPath, ByKindAndName, ByAffectedId).
40pub trait ResolveTargetSymbol {
41    /// Resolve MutationTargetSymbol to SymbolId.
42    ///
43    /// Called during Wave execution (DetectConflict phase).
44    fn resolve_target_symbol(
45        &self,
46        target: &MutationTargetSymbol,
47        ctx: &AnalysisContext,
48    ) -> Result<SymbolId, ConvertError> {
49        match target {
50            // Already resolved
51            MutationTargetSymbol::ById(id) => {
52                // Verify it exists in registry
53                if ctx.registry.resolve(*id).is_none() {
54                    return Err(ConvertError::TargetNotFound(format!(
55                        "SymbolId {:?} not found in registry",
56                        id
57                    )));
58                }
59                Ok(*id)
60            }
61
62            // Lazy resolution by path
63            MutationTargetSymbol::ByPath(path) => {
64                // Lookup symbol by path
65                ctx.registry
66                    .iter()
67                    .find(|(_, p)| *p == &**path)
68                    .map(|(id, _)| id)
69                    .ok_or_else(|| {
70                        ConvertError::TargetNotFound(format!("Symbol not found by path: {}", path))
71                    })
72            }
73
74            // Lazy resolution by kind + name
75            MutationTargetSymbol::ByKindAndName(kind, name) => {
76                // Search for symbol matching kind and name
77                // For impl blocks with generics, normalize both names before comparison
78                // (registry stores "Generic < T , U >" but DSL specifies "Generic<T, U>")
79                let normalized_name = normalize_generic_name(name);
80                let matches: Vec<_> = ctx
81                    .registry
82                    .iter()
83                    .filter(|(_, path)| normalize_generic_name(path.name()) == normalized_name)
84                    .collect();
85
86                match matches.len() {
87                    0 => Err(ConvertError::TargetNotFound(format!(
88                        "Symbol not found: kind={:?}, name={}",
89                        kind, name
90                    ))),
91                    1 => Ok(matches[0].0),
92                    _ => Err(ConvertError::TargetNotFound(format!(
93                        "Multiple symbols found: kind={:?}, name={} (found {} matches)",
94                        kind,
95                        name,
96                        matches.len()
97                    ))),
98                }
99            }
100
101            // Derived from parent (e.g., field in newly added struct)
102            MutationTargetSymbol::ByAffectedId {
103                parent_id,
104                kind,
105                name,
106            } => {
107                // For now, we need the parent to exist
108                // In future, this could track mutations within the same Wave
109                let parent_path = ctx.registry.resolve(*parent_id).ok_or_else(|| {
110                    ConvertError::TargetNotFound(format!(
111                        "Parent SymbolId {:?} not found",
112                        parent_id
113                    ))
114                })?;
115
116                if let Some(child_name) = name {
117                    // Construct child path
118                    parent_path
119                        .child(child_name)
120                        .map_err(|e| {
121                            ConvertError::TargetNotFound(format!("Invalid child path: {}", e))
122                        })
123                        .and_then(|child_path| {
124                            // Lookup child in registry
125                            ctx.registry
126                                .iter()
127                                .find(|(_, p)| p == &&child_path)
128                                .map(|(id, _)| id)
129                                .ok_or_else(|| {
130                                    ConvertError::TargetNotFound(format!(
131                                        "Child symbol not found: parent={:?}, kind={:?}, name={}",
132                                        parent_id, kind, child_name
133                                    ))
134                                })
135                        })
136                } else {
137                    // Anonymous child - not yet supported
138                    Err(ConvertError::TargetNotFound(format!(
139                        "Anonymous child symbols not yet supported: parent={:?}, kind={:?}",
140                        parent_id, kind
141                    )))
142                }
143            }
144        }
145    }
146}
147
148/// Normalize generic type names for comparison.
149///
150/// The registry stores generic types with spaces due to `to_token_stream().to_string()`
151/// behavior (e.g., "Generic < T , U >"), but DSL specifies them without spaces
152/// (e.g., "Generic<T, U>"). This function normalizes both formats for comparison.
153fn normalize_generic_name(name: &str) -> String {
154    name.replace(" < ", "<")
155        .replace(" > ", ">")
156        .replace("< ", "<")
157        .replace(" >", ">")
158        .replace(", ", ",")
159        .replace(" ,", ",")
160}
161
162// Phase 1 converters
163pub mod derive;
164pub mod field;
165pub mod rename;
166pub mod visibility;
167
168// Phase 2 converters
169pub mod add_item;
170pub mod enum_;
171pub mod method;
172pub mod module;
173pub mod remove;
174
175// Phase 3 converters
176pub mod duplicate;
177pub mod idiom;
178pub mod match_arm;
179pub mod move_;
180pub mod plugin;
181pub mod stmt;
182pub mod struct_literal_field;
183pub mod trait_;
184
185// Re-exports
186pub use add_item::AddItemConverter;
187pub use derive::DeriveConverter;
188pub use duplicate::DuplicateConverter;
189pub use enum_::EnumConverter;
190pub use field::FieldConverter;
191pub use idiom::IdiomConverter;
192pub use match_arm::MatchArmConverter;
193pub use method::MethodConverter;
194pub use module::ModuleConverter;
195pub use move_::MoveConverter;
196pub use plugin::PluginConverter;
197pub use remove::RemoveConverter;
198pub use rename::RenameConverter;
199pub use stmt::StmtConverter;
200pub use struct_literal_field::StructLiteralFieldConverter;
201pub use trait_::TraitConverter;
202pub use visibility::VisibilityConverter;