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 deduplicate,
81 merge_groups,
82 ..
83 } => {
84 let mutation = OrganizeImportsMutation::new()
85 .with_deduplicate(*deduplicate)
86 .with_merge_groups(*merge_groups);
87 Ok(vec![Box::new(mutation)])
88 }
89
90 MutationSpec::LoopToIterator { target_var, .. } => {
91 let mut mutation = LoopToIteratorMutation::new();
92 if let Some(var) = target_var {
93 mutation = mutation.with_target(var);
94 }
95 Ok(vec![Box::new(mutation)])
96 }
97
98 MutationSpec::UnwrapToQuestion {
99 target_fn,
100 include_expect,
101 ..
102 } => {
103 let mut mutation = UnwrapToQuestionMutation::new();
104 if !include_expect {
105 mutation = mutation.unwrap_only();
106 }
107 if let Some(fn_id) = target_fn {
108 mutation = mutation.in_function(*fn_id);
109 }
110 Ok(vec![Box::new(mutation)])
111 }
112
113 MutationSpec::AssignOp { fn_id, .. } => {
114 let mut mutation = AssignOpMutation::new();
115 if let Some(id) = fn_id {
116 mutation = mutation.in_function(*id);
117 }
118 Ok(vec![Box::new(mutation)])
119 }
120
121 MutationSpec::BoolSimplify { .. } => Ok(vec![Box::new(BoolSimplifyMutation::new())]),
122
123 MutationSpec::CloneOnCopy { .. } => Ok(vec![Box::new(CloneOnCopyMutation::new())]),
124
125 MutationSpec::CollapsibleIf { .. } => Ok(vec![Box::new(CollapsibleIfMutation::new())]),
126
127 MutationSpec::ComparisonToMethod { .. } => {
128 Ok(vec![Box::new(ComparisonToMethodMutation::new())])
129 }
130
131 MutationSpec::RedundantClosure { .. } => {
132 Ok(vec![Box::new(RedundantClosureMutation::new())])
133 }
134
135 MutationSpec::IntroduceVariable { expr, var_name, .. } => {
136 let syn_expr: syn::Expr = syn::parse_str(expr).map_err(|e| {
137 ConvertError::Parse(format!("Failed to parse expression '{}': {}", expr, e))
138 })?;
139 let pure_expr = syn_expr.to_pure();
140 Ok(vec![Box::new(IntroduceVariableMutation::new(
141 pure_expr, var_name,
142 ))])
143 }
144
145 MutationSpec::ManualMap { .. } => Ok(vec![Box::new(ManualMapMutation::new())]),
146
147 MutationSpec::MatchToIfLet { .. } => Ok(vec![Box::new(MatchToIfLetMutation::new())]),
148
149 MutationSpec::FilterNext { fn_id, .. } => {
150 let mut m = FilterNextMutation::new();
151 if let Some(id) = fn_id {
152 m = m.in_function(*id);
153 }
154 Ok(vec![Box::new(m)])
155 }
156
157 MutationSpec::MapUnwrapOr { fn_id, .. } => {
158 let mut m = MapUnwrapOrMutation::new();
159 if let Some(id) = fn_id {
160 m = m.in_function(*id);
161 }
162 Ok(vec![Box::new(m)])
163 }
164
165 MutationSpec::NoOpArmToTodo {
166 module_id,
167 replacement,
168 } => {
169 let mut m = NoOpArmToTodoMutation::new().with_replacement(replacement);
170 if let Some(id) = module_id {
171 m = m.in_function(*id);
172 }
173 Ok(vec![Box::new(m)])
174 }
175
176 _ => Err(ConvertError::TypeMismatch {
177 expected: "Idiom transformation",
178 actual: spec.kind_name().to_string(),
179 }),
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_idiom_converter_spec_kinds() {
190 let converter = IdiomConverter::new();
191 assert_eq!(converter.spec_kinds().len(), 15); assert!(converter.spec_kinds().contains(&"OrganizeImports"));
193 assert!(converter.spec_kinds().contains(&"LoopToIterator"));
194 assert!(converter.spec_kinds().contains(&"MatchToIfLet"));
195 assert!(converter.spec_kinds().contains(&"NoOpArmToTodo"));
196 }
197}