ryo-executor 0.1.0

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

use ryo_mutations::basic::{AddUseMutation, RemoveUseMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::{PureItem, PureUse, PureUseTree, PureVis};
use ryo_symbol::SymbolKind;

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

impl ASTRegApply for AddUseMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        // Find crate root module
        let module_id = ctx
            .symbol_registry
            .iter()
            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Mod))
            .find(|(_, path)| path.to_string() == "crate")
            .map(|(id, _)| id);

        let module_id = match module_id {
            Some(id) => id,
            None => {
                return MutationResult {
                    mutation_type: "AddUse".to_string(),
                    changes: 0,
                    description: "Crate root module not found".to_string(),
                };
            }
        };

        // Check if use statement already exists
        let exists = ctx
            .symbol_registry
            .iter()
            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Use))
            .any(|(_, path)| path.name() == self.path);

        if exists {
            return MutationResult {
                mutation_type: "AddUse".to_string(),
                changes: 0,
                description: format!("Use statement '{}' already exists", self.path),
            };
        }

        // Parse path into PureUseTree
        let tree = parse_path_to_tree(&self.path);

        // Create the use statement
        let use_item = PureUse {
            vis: PureVis::Private,
            tree,
        };

        // Use the path as the name for registration
        let use_path = ctx
            .symbol_registry
            .path(module_id)
            .and_then(|p| p.child(&self.path).ok());

        if let Some(path) = use_path {
            if let Ok(use_id) = ctx.symbol_registry.register(path, SymbolKind::Use) {
                ctx.ast_registry.set(use_id, PureItem::Use(use_item));

                return MutationResult {
                    mutation_type: "AddUse".to_string(),
                    changes: 1,
                    description: format!("Added use statement '{}'", self.path),
                };
            }
        }

        MutationResult {
            mutation_type: "AddUse".to_string(),
            changes: 0,
            description: format!("Failed to register use statement '{}'", self.path),
        }
    }
}

impl ASTRegApply for RemoveUseMutation {
    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
        let use_id = ctx
            .symbol_registry
            .iter()
            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Use))
            .find(|(_, path)| {
                if self.path.contains("::") {
                    path.to_string() == self.path || path.name() == self.path
                } else {
                    path.name() == self.path
                }
            })
            .map(|(id, _)| id);

        let use_id = match use_id {
            Some(id) => id,
            None => {
                return MutationResult {
                    mutation_type: "RemoveUse".to_string(),
                    changes: 0,
                    description: format!("Use statement '{}' not found", self.path),
                };
            }
        };

        ctx.ast_registry.remove(use_id);

        MutationResult {
            mutation_type: "RemoveUse".to_string(),
            changes: 1,
            description: format!("Removed use statement '{}'", self.path),
        }
    }
}

/// Parse a path string into PureUseTree
fn parse_path_to_tree(path: &str) -> PureUseTree {
    let parts: Vec<&str> = path.split("::").collect();
    if parts.is_empty() {
        return PureUseTree::Name(path.to_string());
    }

    // Build tree from right to left
    let mut tree = PureUseTree::Name(
        parts
            .last()
            .expect("is_empty() guard above guarantees a last element")
            .to_string(),
    );
    for part in parts.iter().rev().skip(1) {
        tree = PureUseTree::Path {
            path: part.to_string(),
            tree: Box::new(tree),
        };
    }
    tree
}