ryo_executor/executor/registry/converters/
idiom.rs1use crate::engine::ASTRegApply;
21use crate::executor::registry::converter::{ConvertError, MutationConverter};
22use crate::executor::registry::converters::ResolveTargetSymbol;
23use crate::executor::spec::MutationSpec;
24use ryo_analysis::AnalysisContext;
25use ryo_mutations::{
26 AssignOpMutation, BoolSimplifyMutation, CloneOnCopyMutation, CollapsibleIfMutation,
27 ComparisonToMethodMutation, FilterNextMutation, IntroduceVariableMutation,
28 LoopToIteratorMutation, ManualMapMutation, MapUnwrapOrMutation, MatchToIfLetMutation,
29 NoOpArmToTodoMutation, OrganizeImportsMutation, RedundantClosureMutation,
30 UnwrapToQuestionMutation,
31};
32use ryo_source::pure::ToPure;
33
34pub struct IdiomConverter;
36
37impl IdiomConverter {
38 pub fn new() -> Self {
39 Self
40 }
41}
42
43impl Default for IdiomConverter {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl ResolveTargetSymbol for IdiomConverter {}
51
52impl MutationConverter for IdiomConverter {
53 fn spec_kinds(&self) -> &'static [&'static str] {
54 &[
55 "OrganizeImports",
56 "LoopToIterator",
57 "UnwrapToQuestion",
58 "AssignOp",
59 "BoolSimplify",
60 "CloneOnCopy",
61 "CollapsibleIf",
62 "ComparisonToMethod",
63 "RedundantClosure",
64 "IntroduceVariable",
65 "ManualMap",
66 "MatchToIfLet",
67 "FilterNext",
68 "MapUnwrapOr",
69 "NoOpArmToTodo",
70 ]
71 }
72
73 fn convert_v2(
74 &self,
75 spec: &MutationSpec,
76 _ctx: &AnalysisContext,
77 ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
78 match spec {
79 MutationSpec::OrganizeImports {
80 module_id,
81 deduplicate,
82 merge_groups,
83 } => {
84 let mut mutation = OrganizeImportsMutation::new()
85 .with_deduplicate(*deduplicate)
86 .with_merge_groups(*merge_groups);
87 if let Some(id) = module_id {
88 mutation = mutation.in_module(*id);
89 }
90 Ok(vec![Box::new(mutation)])
91 }
92
93 MutationSpec::LoopToIterator {
94 module_id,
95 target_var,
96 } => {
97 let mut mutation = LoopToIteratorMutation::new();
98 if let Some(var) = target_var {
99 mutation = mutation.with_target(var);
100 }
101 if let Some(id) = module_id {
102 mutation = mutation.in_function(*id);
103 }
104 Ok(vec![Box::new(mutation)])
105 }
106
107 MutationSpec::UnwrapToQuestion {
108 target_fn,
109 include_expect,
110 ..
111 } => {
112 let mut mutation = UnwrapToQuestionMutation::new();
113 if !include_expect {
114 mutation = mutation.unwrap_only();
115 }
116 if let Some(fn_id) = target_fn {
117 mutation = mutation.in_function(*fn_id);
118 }
119 Ok(vec![Box::new(mutation)])
120 }
121
122 MutationSpec::AssignOp { fn_id, .. } => {
123 let mut mutation = AssignOpMutation::new();
124 if let Some(id) = fn_id {
125 mutation = mutation.in_function(*id);
126 }
127 Ok(vec![Box::new(mutation)])
128 }
129
130 MutationSpec::BoolSimplify { module_id } => {
131 let mut m = BoolSimplifyMutation::new();
132 if let Some(id) = module_id {
133 m = m.in_function(*id);
134 }
135 Ok(vec![Box::new(m)])
136 }
137
138 MutationSpec::CloneOnCopy { module_id } => {
139 let mut m = CloneOnCopyMutation::new();
140 if let Some(id) = module_id {
141 m = m.in_function(*id);
142 }
143 Ok(vec![Box::new(m)])
144 }
145
146 MutationSpec::CollapsibleIf { module_id } => {
147 let mut m = CollapsibleIfMutation::new();
148 if let Some(id) = module_id {
149 m = m.in_function(*id);
150 }
151 Ok(vec![Box::new(m)])
152 }
153
154 MutationSpec::ComparisonToMethod { module_id } => {
155 let mut m = ComparisonToMethodMutation::new();
156 if let Some(id) = module_id {
157 m = m.in_function(*id);
158 }
159 Ok(vec![Box::new(m)])
160 }
161
162 MutationSpec::RedundantClosure { module_id } => {
163 let mut m = RedundantClosureMutation::new();
164 if let Some(id) = module_id {
165 m = m.in_function(*id);
166 }
167 Ok(vec![Box::new(m)])
168 }
169
170 MutationSpec::IntroduceVariable { expr, var_name, .. } => {
171 let syn_expr: syn::Expr = syn::parse_str(expr).map_err(|e| {
172 ConvertError::Parse(format!("Failed to parse expression '{}': {}", expr, e))
173 })?;
174 let pure_expr = syn_expr.to_pure();
175 Ok(vec![Box::new(IntroduceVariableMutation::new(
176 pure_expr, var_name,
177 ))])
178 }
179
180 MutationSpec::ManualMap { module_id } => {
181 let mut m = ManualMapMutation::new();
182 if let Some(id) = module_id {
183 m = m.in_function(*id);
184 }
185 Ok(vec![Box::new(m)])
186 }
187
188 MutationSpec::MatchToIfLet { module_id } => {
189 let mut m = MatchToIfLetMutation::new();
190 if let Some(id) = module_id {
191 m = m.in_function(*id);
192 }
193 Ok(vec![Box::new(m)])
194 }
195
196 MutationSpec::FilterNext { fn_id, .. } => {
197 let mut m = FilterNextMutation::new();
198 if let Some(id) = fn_id {
199 m = m.in_function(*id);
200 }
201 Ok(vec![Box::new(m)])
202 }
203
204 MutationSpec::MapUnwrapOr { fn_id, .. } => {
205 let mut m = MapUnwrapOrMutation::new();
206 if let Some(id) = fn_id {
207 m = m.in_function(*id);
208 }
209 Ok(vec![Box::new(m)])
210 }
211
212 MutationSpec::NoOpArmToTodo {
213 module_id,
214 replacement,
215 } => {
216 let mut m = NoOpArmToTodoMutation::new().with_replacement(replacement);
217 if let Some(id) = module_id {
218 m = m.in_function(*id);
219 }
220 Ok(vec![Box::new(m)])
221 }
222
223 _ => Err(ConvertError::TypeMismatch {
224 expected: "Idiom transformation",
225 actual: spec.kind_name().to_string(),
226 }),
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_idiom_converter_spec_kinds() {
237 let converter = IdiomConverter::new();
238 assert_eq!(converter.spec_kinds().len(), 15); assert!(converter.spec_kinds().contains(&"OrganizeImports"));
240 assert!(converter.spec_kinds().contains(&"LoopToIterator"));
241 assert!(converter.spec_kinds().contains(&"MatchToIfLet"));
242 assert!(converter.spec_kinds().contains(&"NoOpArmToTodo"));
243 }
244}