use crate::engine::ASTRegApply;
use crate::executor::registry::converter::{ConvertError, MutationConverter};
use crate::executor::registry::converters::ResolveTargetSymbol;
use crate::executor::spec::MutationSpec;
use ryo_analysis::AnalysisContext;
use ryo_mutations::{
AssignOpMutation, BoolSimplifyMutation, CloneOnCopyMutation, CollapsibleIfMutation,
ComparisonToMethodMutation, FilterNextMutation, IntroduceVariableMutation,
LoopToIteratorMutation, ManualMapMutation, MapUnwrapOrMutation, MatchToIfLetMutation,
NoOpArmToTodoMutation, OrganizeImportsMutation, RedundantClosureMutation,
UnwrapToQuestionMutation,
};
use ryo_source::pure::ToPure;
pub struct IdiomConverter;
impl IdiomConverter {
pub fn new() -> Self {
Self
}
}
impl Default for IdiomConverter {
fn default() -> Self {
Self::new()
}
}
impl ResolveTargetSymbol for IdiomConverter {}
impl MutationConverter for IdiomConverter {
fn spec_kinds(&self) -> &'static [&'static str] {
&[
"OrganizeImports",
"LoopToIterator",
"UnwrapToQuestion",
"AssignOp",
"BoolSimplify",
"CloneOnCopy",
"CollapsibleIf",
"ComparisonToMethod",
"RedundantClosure",
"IntroduceVariable",
"ManualMap",
"MatchToIfLet",
"FilterNext",
"MapUnwrapOr",
"NoOpArmToTodo",
]
}
fn convert_v2(
&self,
spec: &MutationSpec,
_ctx: &AnalysisContext,
) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
match spec {
MutationSpec::OrganizeImports {
deduplicate,
merge_groups,
..
} => {
let mutation = OrganizeImportsMutation::new()
.with_deduplicate(*deduplicate)
.with_merge_groups(*merge_groups);
Ok(vec![Box::new(mutation)])
}
MutationSpec::LoopToIterator { target_var, .. } => {
let mut mutation = LoopToIteratorMutation::new();
if let Some(var) = target_var {
mutation = mutation.with_target(var);
}
Ok(vec![Box::new(mutation)])
}
MutationSpec::UnwrapToQuestion {
target_fn,
include_expect,
..
} => {
let mut mutation = UnwrapToQuestionMutation::new();
if !include_expect {
mutation = mutation.unwrap_only();
}
if let Some(fn_id) = target_fn {
mutation = mutation.in_function(*fn_id);
}
Ok(vec![Box::new(mutation)])
}
MutationSpec::AssignOp { fn_id, .. } => {
let mut mutation = AssignOpMutation::new();
if let Some(id) = fn_id {
mutation = mutation.in_function(*id);
}
Ok(vec![Box::new(mutation)])
}
MutationSpec::BoolSimplify { .. } => Ok(vec![Box::new(BoolSimplifyMutation::new())]),
MutationSpec::CloneOnCopy { .. } => Ok(vec![Box::new(CloneOnCopyMutation::new())]),
MutationSpec::CollapsibleIf { .. } => Ok(vec![Box::new(CollapsibleIfMutation::new())]),
MutationSpec::ComparisonToMethod { .. } => {
Ok(vec![Box::new(ComparisonToMethodMutation::new())])
}
MutationSpec::RedundantClosure { .. } => {
Ok(vec![Box::new(RedundantClosureMutation::new())])
}
MutationSpec::IntroduceVariable { expr, var_name, .. } => {
let syn_expr: syn::Expr = syn::parse_str(expr).map_err(|e| {
ConvertError::Parse(format!("Failed to parse expression '{}': {}", expr, e))
})?;
let pure_expr = syn_expr.to_pure();
Ok(vec![Box::new(IntroduceVariableMutation::new(
pure_expr, var_name,
))])
}
MutationSpec::ManualMap { .. } => Ok(vec![Box::new(ManualMapMutation::new())]),
MutationSpec::MatchToIfLet { .. } => Ok(vec![Box::new(MatchToIfLetMutation::new())]),
MutationSpec::FilterNext { fn_id, .. } => {
let mut m = FilterNextMutation::new();
if let Some(id) = fn_id {
m = m.in_function(*id);
}
Ok(vec![Box::new(m)])
}
MutationSpec::MapUnwrapOr { fn_id, .. } => {
let mut m = MapUnwrapOrMutation::new();
if let Some(id) = fn_id {
m = m.in_function(*id);
}
Ok(vec![Box::new(m)])
}
MutationSpec::NoOpArmToTodo {
module_id,
replacement,
} => {
let mut m = NoOpArmToTodoMutation::new().with_replacement(replacement);
if let Some(id) = module_id {
m = m.in_function(*id);
}
Ok(vec![Box::new(m)])
}
_ => Err(ConvertError::TypeMismatch {
expected: "Idiom transformation",
actual: spec.kind_name().to_string(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_idiom_converter_spec_kinds() {
let converter = IdiomConverter::new();
assert_eq!(converter.spec_kinds().len(), 15); assert!(converter.spec_kinds().contains(&"OrganizeImports"));
assert!(converter.spec_kinds().contains(&"LoopToIterator"));
assert!(converter.spec_kinds().contains(&"MatchToIfLet"));
assert!(converter.spec_kinds().contains(&"NoOpArmToTodo"));
}
}