Skip to main content

ryo_analysis/cascade/
strategy.rs

1//! Cascade strategies for handling different mutation scenarios.
2//!
3//! Each strategy controls how the cascade engine handles specific situations
4//! like derive failures, match exhaustiveness, field initialization, etc.
5
6use std::path::PathBuf;
7
8/// Strategy for handling derive failures.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum DeriveStrategy {
11    /// Try to add derive to dependent types recursively.
12    #[default]
13    TryAddDerive,
14
15    /// Generate manual impl blocks instead of derive.
16    GenerateImpl,
17
18    /// Skip the failing type and report it.
19    SkipAndReport,
20
21    /// Stop immediately on first failure.
22    ImmediateError,
23}
24
25/// Strategy for updating match expressions when adding enum variants.
26#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub enum MatchUpdateStrategy {
28    /// Add a wildcard arm: `_ => { todo!() }`
29    #[default]
30    AddWildcard,
31
32    /// Add an explicit arm with the specified body.
33    AddExplicitArm(String),
34
35    /// Add a todo arm: `NewVariant => todo!()`
36    AddTodo,
37
38    /// Add unreachable arm: `NewVariant => unreachable!()`
39    AddUnreachable,
40
41    /// Prompt user for each match expression.
42    Interactive,
43}
44
45/// Strategy for initializing new fields when adding to structs.
46#[derive(Debug, Clone, PartialEq, Eq, Default)]
47pub enum FieldInitStrategy {
48    /// Use `Default::default()` or `None` for Option types.
49    #[default]
50    UseDefault,
51
52    /// Use a specific value.
53    WithValue(String),
54
55    /// Add the field as a new constructor parameter.
56    AddToConstructor,
57
58    /// Use a builder pattern.
59    UseBuilder,
60
61    /// Prompt user for each initialization site.
62    Interactive,
63}
64
65/// Strategy for updating references during rename or removal.
66#[derive(Debug, Clone, PartialEq, Eq, Default)]
67pub enum ReferenceUpdateStrategy {
68    /// Update all references across the workspace.
69    #[default]
70    UpdateAll,
71
72    /// Only update references within the same crate.
73    SameCrateOnly,
74
75    /// Only update references in specific files.
76    SpecificFiles(Vec<PathBuf>),
77
78    /// Don't update, just report what would change.
79    ReportOnly,
80}
81
82/// Strategy for handling field/variant removal.
83#[derive(Debug, Clone, PartialEq, Eq, Default)]
84pub enum RemovalStrategy {
85    /// Remove all references (dangerous).
86    RemoveAllReferences,
87
88    /// Just report affected locations, don't modify.
89    #[default]
90    ReportOnly,
91
92    /// Add deprecation warning first, remove later.
93    DeprecateFirst {
94        /// The deprecation warning message.
95        warning_message: String,
96    },
97
98    /// Migrate to a replacement field/variant.
99    MigrateTo {
100        /// The replacement to use.
101        replacement: String,
102    },
103}
104
105/// Strategy for handling errors during cascade execution.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
107pub enum ErrorStrategy {
108    /// Stop execution and report the error.
109    #[default]
110    StopAndReport,
111
112    /// Log a warning and continue with remaining steps.
113    ContinueWithWarning,
114
115    /// Skip the failed step silently and continue.
116    SkipAndContinue,
117
118    /// Rollback all applied changes and report.
119    Rollback,
120}
121
122/// Unified cascade strategy combining all individual strategies.
123#[derive(Debug, Clone, Default)]
124pub struct CascadeStrategy {
125    /// Strategy for derive operations.
126    pub derive: DeriveStrategy,
127
128    /// Strategy for match expression updates.
129    pub match_update: MatchUpdateStrategy,
130
131    /// Strategy for field initialization.
132    pub field_init: FieldInitStrategy,
133
134    /// Strategy for reference updates.
135    pub reference_update: ReferenceUpdateStrategy,
136
137    /// Strategy for removal operations.
138    pub removal: RemovalStrategy,
139
140    /// Strategy for error handling.
141    pub on_error: ErrorStrategy,
142}
143
144impl CascadeStrategy {
145    /// Create a safe strategy that prioritizes data protection.
146    ///
147    /// - Deprecates before removing
148    /// - Stops on errors
149    /// - Reports rather than modifies for dangerous operations
150    pub fn safe() -> Self {
151        Self {
152            derive: DeriveStrategy::TryAddDerive,
153            match_update: MatchUpdateStrategy::AddTodo,
154            field_init: FieldInitStrategy::UseDefault,
155            reference_update: ReferenceUpdateStrategy::UpdateAll,
156            removal: RemovalStrategy::DeprecateFirst {
157                warning_message: "This item will be removed in a future version".to_string(),
158            },
159            on_error: ErrorStrategy::StopAndReport,
160        }
161    }
162
163    /// Create an aggressive strategy that prioritizes automation.
164    ///
165    /// - Removes references directly
166    /// - Continues past errors
167    /// - Applies changes automatically
168    pub fn aggressive() -> Self {
169        Self {
170            derive: DeriveStrategy::TryAddDerive,
171            match_update: MatchUpdateStrategy::AddWildcard,
172            field_init: FieldInitStrategy::UseDefault,
173            reference_update: ReferenceUpdateStrategy::UpdateAll,
174            removal: RemovalStrategy::RemoveAllReferences,
175            on_error: ErrorStrategy::ContinueWithWarning,
176        }
177    }
178
179    /// Create an interactive strategy that prompts for decisions.
180    pub fn interactive() -> Self {
181        Self {
182            derive: DeriveStrategy::TryAddDerive,
183            match_update: MatchUpdateStrategy::Interactive,
184            field_init: FieldInitStrategy::Interactive,
185            reference_update: ReferenceUpdateStrategy::UpdateAll,
186            removal: RemovalStrategy::ReportOnly,
187            on_error: ErrorStrategy::StopAndReport,
188        }
189    }
190
191    /// Create an eager strategy that propagates changes recursively.
192    pub fn eager() -> Self {
193        Self::default()
194    }
195
196    /// Create a lazy strategy that only applies direct changes.
197    pub fn lazy() -> Self {
198        Self {
199            derive: DeriveStrategy::SkipAndReport,
200            ..Self::default()
201        }
202    }
203
204    /// Check if this strategy uses eager propagation.
205    pub fn is_eager(&self) -> bool {
206        matches!(
207            self.derive,
208            DeriveStrategy::TryAddDerive | DeriveStrategy::GenerateImpl
209        )
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn test_default_strategy() {
219        let strategy = CascadeStrategy::default();
220        assert_eq!(strategy.derive, DeriveStrategy::TryAddDerive);
221        assert_eq!(strategy.match_update, MatchUpdateStrategy::AddWildcard);
222        assert_eq!(strategy.on_error, ErrorStrategy::StopAndReport);
223    }
224
225    #[test]
226    fn test_safe_strategy() {
227        let strategy = CascadeStrategy::safe();
228        assert!(matches!(
229            strategy.removal,
230            RemovalStrategy::DeprecateFirst { .. }
231        ));
232        assert_eq!(strategy.on_error, ErrorStrategy::StopAndReport);
233    }
234
235    #[test]
236    fn test_aggressive_strategy() {
237        let strategy = CascadeStrategy::aggressive();
238        assert_eq!(strategy.removal, RemovalStrategy::RemoveAllReferences);
239        assert_eq!(strategy.on_error, ErrorStrategy::ContinueWithWarning);
240    }
241}