ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! V2 ASTRegApply implementations for Default trait mutations
//!
//! - DefaultMutation: Add #[derive(Default)] or impl Default for structs
//! - DeriveDefaultMutation: Convert manual Default impl to #[derive(Default)]

use ryo_mutations::idiom::{DefaultMutation, DeriveDefaultMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::PureItem;
use ryo_symbol::SymbolKind;

use crate::engine::{ASTMutationContext, ASTRegApply};

impl ASTRegApply for DefaultMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        let mut total_changes = 0;

        // Find structs that need Default
        let struct_ids: Vec<_> = ctx
            .symbol_registry
            .iter()
            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
            .map(|(id, path)| (id, path.name().to_string()))
            .collect();

        for (struct_id, struct_name) in struct_ids {
            // Apply target filter
            if let Some(ref target) = self.target_struct {
                if &struct_name != target {
                    continue;
                }
            }

            if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
                // Skip if already has derive(Default)
                let has_default = s.attrs.iter().any(|attr| {
                    attr.path == "derive"
                        && matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
                });

                if has_default {
                    continue;
                }

                // Only named-field structs
                let has_named_fields = matches!(&s.fields, ryo_source::pure::PureFields::Named(_));
                if !has_named_fields {
                    continue;
                }

                let mut new_struct = s.clone();

                if self.use_derive {
                    // Add derive(Default)
                    let derive_attr_idx = new_struct
                        .attrs
                        .iter()
                        .position(|attr| attr.path == "derive");

                    if let Some(idx) = derive_attr_idx {
                        if let ryo_source::pure::PureAttrMeta::List(ref args) =
                            new_struct.attrs[idx].meta
                        {
                            if !args.contains("Default") {
                                new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
                                    format!("{}, Default", args),
                                );
                            }
                        }
                    } else {
                        new_struct.attrs.push(ryo_source::pure::PureAttribute {
                            path: "derive".to_string(),
                            meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
                            is_inner: false,
                        });
                    }

                    ctx.set_ast(struct_id, PureItem::Struct(new_struct));
                    total_changes += 1;
                }
                // Note: impl Default generation would require more complex code
                // For now, only derive is supported in V2
            }
        }

        MutationResult {
            mutation_type: "Default".to_string(),
            changes: total_changes,
            description: if total_changes > 0 {
                format!("Added #[derive(Default)] to {} struct(s)", total_changes)
            } else {
                "No Default implementations added".to_string()
            },
        }
    }
}

impl ASTRegApply for DeriveDefaultMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        // Find Default impl blocks that can be converted to derive
        let mut types_to_derive: Vec<(ryo_symbol::SymbolId, String)> = Vec::new();

        let impl_ids: Vec<_> = ctx
            .symbol_registry
            .iter()
            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Impl))
            .map(|(id, _)| id)
            .collect();

        for impl_id in impl_ids {
            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
                // Check if this is a Default impl
                if imp.trait_.as_deref() != Some("Default") {
                    continue;
                }

                if let Some(ref target) = self.target_type {
                    if &imp.self_ty != target {
                        continue;
                    }
                }

                // For simplicity, just mark for derive if it's a Default impl
                // A full implementation would check if all fields use default values
                types_to_derive.push((impl_id, imp.self_ty.clone()));
            }
        }

        if types_to_derive.is_empty() {
            return MutationResult {
                mutation_type: "DeriveDefault".to_string(),
                changes: 0,
                description: "No derivable Default implementations found".to_string(),
            };
        }

        let mut changes = 0;

        // Add derive(Default) to structs and remove impl blocks
        for (impl_id, type_name) in &types_to_derive {
            // Find the struct
            let struct_id = ctx
                .symbol_registry
                .iter()
                .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
                .find(|(_, path)| path.name() == type_name)
                .map(|(id, _)| id);

            if let Some(struct_id) = struct_id {
                if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
                    let mut new_struct = s.clone();

                    // Check if derive(Default) already exists
                    let has_default = new_struct.attrs.iter().any(|attr| {
                        attr.path == "derive"
                            && matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
                    });

                    if !has_default {
                        // Add derive(Default) - merge with existing derive if present
                        let derive_attr_idx = new_struct
                            .attrs
                            .iter()
                            .position(|attr| attr.path == "derive");

                        if let Some(idx) = derive_attr_idx {
                            if let ryo_source::pure::PureAttrMeta::List(ref args) =
                                new_struct.attrs[idx].meta
                            {
                                new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
                                    format!("{}, Default", args),
                                );
                            }
                        } else {
                            new_struct.attrs.push(ryo_source::pure::PureAttribute {
                                path: "derive".to_string(),
                                meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
                                is_inner: false,
                            });
                        }

                        ctx.set_ast(struct_id, PureItem::Struct(new_struct));
                        changes += 1;
                    }
                }
            }

            // Remove the Default impl block
            ctx.ast_registry.remove(*impl_id);
            changes += 1;
        }

        MutationResult {
            mutation_type: "DeriveDefault".to_string(),
            changes,
            description: format!(
                "Converted {} manual Default impl(s) to #[derive(Default)]",
                types_to_derive.len()
            ),
        }
    }
}