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;