ryo-analysis 0.1.0

Code graph and discovery engine for the RYO project
Documentation
//! Impact analysis for cascade operations.
//!
//! This module provides types for analyzing the impact of mutations
//! on the codebase, including affected symbols, files, and risk assessment.

use std::path::PathBuf;

use crate::SymbolId;

/// Impact level classification.
///
/// Determines how far-reaching a mutation's effects are.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ImpactLevel {
    /// Level 1: Changes only affect the same file.
    /// Example: Expression transformations, local refactoring.
    Local = 1,

    /// Level 2: Changes affect references from other files.
    /// Example: Rename symbol, remove public item.
    Reference = 2,

    /// Level 3: Changes affect type structure.
    /// Example: Add/remove field, add/remove variant.
    Structural = 3,

    /// Level 4: Changes affect type semantics or behavior.
    /// Example: Add derive(Copy), change visibility.
    Semantic = 4,
}

impl ImpactLevel {
    /// Returns a human-readable description of the impact level.
    pub fn description(&self) -> &'static str {
        match self {
            Self::Local => "Local (same file only)",
            Self::Reference => "Reference (cross-file references)",
            Self::Structural => "Structural (type structure changes)",
            Self::Semantic => "Semantic (behavior changes)",
        }
    }
}

/// Set of impacted symbols for cascade child generation.
#[derive(Debug, Default)]
pub struct ImpactSet {
    /// Symbol IDs that need to be updated.
    pub symbols: Vec<SymbolId>,

    /// File paths that contain affected code.
    pub files: Vec<PathBuf>,
}

impl ImpactSet {
    /// Create a new empty impact set.
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a symbol to the impact set.
    pub fn add_symbol(&mut self, symbol: SymbolId) {
        if !self.symbols.contains(&symbol) {
            self.symbols.push(symbol);
        }
    }

    /// Add a file to the impact set.
    pub fn add_file(&mut self, file: PathBuf) {
        if !self.files.contains(&file) {
            self.files.push(file);
        }
    }

    /// Check if the impact set is empty.
    pub fn is_empty(&self) -> bool {
        self.symbols.is_empty() && self.files.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_impact_level_ordering() {
        assert!(ImpactLevel::Local < ImpactLevel::Reference);
        assert!(ImpactLevel::Reference < ImpactLevel::Structural);
        assert!(ImpactLevel::Structural < ImpactLevel::Semantic);
    }

    #[test]
    fn test_impact_set() {
        let mut set = ImpactSet::new();
        assert!(set.is_empty());

        set.add_file(PathBuf::from("test.rs"));
        assert!(!set.is_empty());
    }
}