Skip to main content

ryo_analysis/cascade/
impact.rs

1//! Impact analysis for cascade operations.
2//!
3//! This module provides types for analyzing the impact of mutations
4//! on the codebase, including affected symbols, files, and risk assessment.
5
6use std::path::PathBuf;
7
8use crate::SymbolId;
9
10/// Impact level classification.
11///
12/// Determines how far-reaching a mutation's effects are.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum ImpactLevel {
15    /// Level 1: Changes only affect the same file.
16    /// Example: Expression transformations, local refactoring.
17    Local = 1,
18
19    /// Level 2: Changes affect references from other files.
20    /// Example: Rename symbol, remove public item.
21    Reference = 2,
22
23    /// Level 3: Changes affect type structure.
24    /// Example: Add/remove field, add/remove variant.
25    Structural = 3,
26
27    /// Level 4: Changes affect type semantics or behavior.
28    /// Example: Add derive(Copy), change visibility.
29    Semantic = 4,
30}
31
32impl ImpactLevel {
33    /// Returns a human-readable description of the impact level.
34    pub fn description(&self) -> &'static str {
35        match self {
36            Self::Local => "Local (same file only)",
37            Self::Reference => "Reference (cross-file references)",
38            Self::Structural => "Structural (type structure changes)",
39            Self::Semantic => "Semantic (behavior changes)",
40        }
41    }
42}
43
44/// Set of impacted symbols for cascade child generation.
45#[derive(Debug, Default)]
46pub struct ImpactSet {
47    /// Symbol IDs that need to be updated.
48    pub symbols: Vec<SymbolId>,
49
50    /// File paths that contain affected code.
51    pub files: Vec<PathBuf>,
52}
53
54impl ImpactSet {
55    /// Create a new empty impact set.
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// Add a symbol to the impact set.
61    pub fn add_symbol(&mut self, symbol: SymbolId) {
62        if !self.symbols.contains(&symbol) {
63            self.symbols.push(symbol);
64        }
65    }
66
67    /// Add a file to the impact set.
68    pub fn add_file(&mut self, file: PathBuf) {
69        if !self.files.contains(&file) {
70            self.files.push(file);
71        }
72    }
73
74    /// Check if the impact set is empty.
75    pub fn is_empty(&self) -> bool {
76        self.symbols.is_empty() && self.files.is_empty()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_impact_level_ordering() {
86        assert!(ImpactLevel::Local < ImpactLevel::Reference);
87        assert!(ImpactLevel::Reference < ImpactLevel::Structural);
88        assert!(ImpactLevel::Structural < ImpactLevel::Semantic);
89    }
90
91    #[test]
92    fn test_impact_set() {
93        let mut set = ImpactSet::new();
94        assert!(set.is_empty());
95
96        set.add_file(PathBuf::from("test.rs"));
97        assert!(!set.is_empty());
98    }
99}