ryo-mutations 0.1.0

[experimental] Code transformation primitives for Rust source code
Documentation
//! Function mutations: AddFunctionMutation, RemoveFunctionMutation

use ryo_source::pure::PureFile;
use ryo_symbol::SymbolId;

use crate::{Mutation, ValidationIssue, ValidationResult};

/// Add a new function to the file
#[derive(Debug, Clone)]
pub struct AddFunctionMutation {
    /// Parent module SymbolId
    pub parent: SymbolId,
    pub name: String,
    pub params: Vec<(String, String)>, // (name, type)
    pub return_type: Option<String>,
    pub body: String, // Simple expression body
    pub is_pub: bool,
}

impl AddFunctionMutation {
    pub fn new(parent: SymbolId, name: impl Into<String>) -> Self {
        Self {
            parent,
            name: name.into(),
            params: Vec::new(),
            return_type: None,
            body: "todo!()".to_string(),
            is_pub: false,
        }
    }

    pub fn with_params(mut self, params: Vec<(String, String)>) -> Self {
        self.params = params;
        self
    }

    pub fn with_return_type(mut self, ty: impl Into<String>) -> Self {
        self.return_type = Some(ty.into());
        self
    }

    pub fn with_body(mut self, body: impl Into<String>) -> Self {
        self.body = body.into();
        self
    }

    pub fn public(mut self) -> Self {
        self.is_pub = true;
        self
    }
}

impl Mutation for AddFunctionMutation {
    fn validate(&self, file: &PureFile) -> ValidationResult {
        let mut result = ValidationResult::new();

        // Check if function already exists
        if file.find_fn(&self.name).is_some() {
            result.add(ValidationIssue::duplicate_symbol(&self.name));
        }

        result
    }

    fn describe(&self) -> String {
        let vis = if self.is_pub { "pub " } else { "" };
        let ret = self
            .return_type
            .as_ref()
            .map(|t| format!(" -> {}", t))
            .unwrap_or_default();
        format!(
            "Add {}fn {}(){} {{ {} }} to {}",
            vis, self.name, ret, self.body, self.parent
        )
    }

    fn mutation_type(&self) -> &'static str {
        "AddFunction"
    }

    fn box_clone(&self) -> Box<dyn Mutation> {
        Box::new(self.clone())
    }
}

/// Remove a function from the file
#[derive(Debug, Clone)]
pub struct RemoveFunctionMutation {
    pub symbol_id: SymbolId,
}

impl RemoveFunctionMutation {
    pub fn new(symbol_id: SymbolId) -> Self {
        Self { symbol_id }
    }
}

impl Mutation for RemoveFunctionMutation {
    fn validate(&self, _file: &PureFile) -> ValidationResult {
        // Validation is minimal since we rely on symbol_id (O(1) lookup)
        // Actual validation happens during AST execution via ASTRegApply
        ValidationResult::new()
    }

    fn describe(&self) -> String {
        format!("Remove function {}", self.symbol_id)
    }

    fn mutation_type(&self) -> &'static str {
        "RemoveFunction"
    }

    fn box_clone(&self) -> Box<dyn Mutation> {
        Box::new(self.clone())
    }
}