ryo-executor 0.1.0

[experimental] Mutation execution engine for RYO - parallel execution, conflict detection, workspace management
Documentation
//! ASTRegApply implementation for derive mutations

use ryo_mutations::basic::{AddDeriveMutation, RemoveDeriveMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::{PureAttrMeta, PureAttribute, PureItem};

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

impl ASTRegApply for AddDeriveMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        let target_id = self.symbol_id;

        // Get the AST for this symbol (O(1) lookup)
        let item = match ctx.ast_registry.get(target_id) {
            Some(item) => item.clone(),
            None => {
                return MutationResult {
                    mutation_type: "AddDerive".to_string(),
                    changes: 0,
                    description: format!("AST not found for SymbolId({:?})", target_id),
                };
            }
        };

        // Modify the item
        let (new_item, changes) = match item {
            PureItem::Struct(mut s) => {
                let changes = add_derive_to_attrs(&mut s.attrs, &self.derives);
                (PureItem::Struct(s), changes)
            }
            PureItem::Enum(mut e) => {
                let mut changes = add_derive_to_attrs(&mut e.attrs, &self.derives);
                // For enum with Default derive, add #[default] to first variant
                if self.derives.contains(&"Default".to_string()) {
                    if let Some(first_variant) = e.variants.first_mut() {
                        let has_default_attr =
                            first_variant.attrs.iter().any(|a| a.path == "default");
                        if !has_default_attr {
                            first_variant.attrs.push(PureAttribute {
                                path: "default".to_string(),
                                meta: PureAttrMeta::Path,
                                is_inner: false,
                            });
                            changes += 1;
                        }
                    }
                }
                (PureItem::Enum(e), changes)
            }
            _ => {
                return MutationResult {
                    mutation_type: "AddDerive".to_string(),
                    changes: 0,
                    description: format!("SymbolId({:?}) is not a struct or enum", target_id),
                };
            }
        };

        if changes > 0 {
            // Update the registry
            ctx.set_ast(target_id, new_item);

            // Emit event for each derive added
            for derive in &self.derives {
                ctx.emit(MutationEvent::SymbolModified {
                    id: target_id,
                    modification: ModificationType::DeriveAdded(derive.clone()),
                });
            }
        }

        MutationResult {
            mutation_type: "AddDerive".to_string(),
            changes,
            description: if changes > 0 {
                format!(
                    "Added derive({}) to SymbolId({:?})",
                    self.derives.join(", "),
                    target_id
                )
            } else {
                "Derives already present".to_string()
            },
        }
    }
}

impl ASTRegApply for RemoveDeriveMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        let target_id = self.symbol_id;

        // Get the AST for this symbol (O(1) lookup)
        let item = match ctx.ast_registry.get(target_id) {
            Some(item) => item.clone(),
            None => {
                return MutationResult {
                    mutation_type: "RemoveDerive".to_string(),
                    changes: 0,
                    description: format!("AST not found for SymbolId({:?})", target_id),
                };
            }
        };

        // Modify the item
        let (new_item, changes) = match item {
            PureItem::Struct(mut s) => {
                let changes = remove_derive_from_attrs(&mut s.attrs, &self.derives);
                (PureItem::Struct(s), changes)
            }
            PureItem::Enum(mut e) => {
                let changes = remove_derive_from_attrs(&mut e.attrs, &self.derives);
                (PureItem::Enum(e), changes)
            }
            _ => {
                return MutationResult {
                    mutation_type: "RemoveDerive".to_string(),
                    changes: 0,
                    description: format!("SymbolId({:?}) is not a struct or enum", target_id),
                };
            }
        };

        if changes > 0 {
            // Update the registry
            ctx.set_ast(target_id, new_item);

            // Emit event for each derive removed
            for derive in &self.derives {
                ctx.emit(MutationEvent::SymbolModified {
                    id: target_id,
                    modification: ModificationType::DeriveRemoved(derive.clone()),
                });
            }
        }

        MutationResult {
            mutation_type: "RemoveDerive".to_string(),
            changes,
            description: if changes > 0 {
                format!(
                    "Removed derive({}) from SymbolId({:?})",
                    self.derives.join(", "),
                    target_id
                )
            } else {
                "Derive not found".to_string()
            },
        }
    }
}

/// Helper to add derive to attrs (shared logic from AddDeriveMutation)
fn add_derive_to_attrs(attrs: &mut Vec<PureAttribute>, derives: &[String]) -> usize {
    let existing_derive_idx = attrs.iter().position(|a| a.path == "derive");

    if let Some(idx) = existing_derive_idx {
        // Extract existing derives from meta
        let existing_args = match &attrs[idx].meta {
            PureAttrMeta::List(args) => args.clone(),
            _ => String::new(),
        };
        let mut all_derives: Vec<String> = existing_args
            .split(',')
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect();

        let mut added = 0;
        for d in derives {
            if !all_derives.contains(d) {
                all_derives.push(d.clone());
                added += 1;
            }
        }

        if added > 0 {
            attrs[idx].meta = PureAttrMeta::List(all_derives.join(", "));
        }
        added
    } else {
        // Create new derive attribute
        attrs.insert(
            0,
            PureAttribute {
                path: "derive".to_string(),
                meta: PureAttrMeta::List(derives.join(", ")),
                is_inner: false,
            },
        );
        derives.len()
    }
}

/// Helper to remove derive from attrs (shared logic from RemoveDeriveMutation)
fn remove_derive_from_attrs(attrs: &mut Vec<PureAttribute>, derives: &[String]) -> usize {
    let existing_derive_idx = attrs.iter().position(|a| a.path == "derive");

    if let Some(idx) = existing_derive_idx {
        // Extract existing derives from meta
        let existing_args = match &attrs[idx].meta {
            PureAttrMeta::List(args) => args.clone(),
            _ => String::new(),
        };
        let remaining: Vec<String> = existing_args
            .split(',')
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty() && !derives.contains(s))
            .collect();

        let original_count = existing_args
            .split(',')
            .filter(|s| !s.trim().is_empty())
            .count();
        let removed = original_count - remaining.len();

        if remaining.is_empty() {
            attrs.remove(idx);
        } else {
            attrs[idx].meta = PureAttrMeta::List(remaining.join(", "));
        }
        removed
    } else {
        0
    }
}