ryo-analysis 0.1.0

Code graph and discovery engine for the RYO project
Documentation
//! Cascade strategies for handling different mutation scenarios.
//!
//! Each strategy controls how the cascade engine handles specific situations
//! like derive failures, match exhaustiveness, field initialization, etc.

use std::path::PathBuf;

/// Strategy for handling derive failures.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DeriveStrategy {
    /// Try to add derive to dependent types recursively.
    #[default]
    TryAddDerive,

    /// Generate manual impl blocks instead of derive.
    GenerateImpl,

    /// Skip the failing type and report it.
    SkipAndReport,

    /// Stop immediately on first failure.
    ImmediateError,
}

/// Strategy for updating match expressions when adding enum variants.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum MatchUpdateStrategy {
    /// Add a wildcard arm: `_ => { todo!() }`
    #[default]
    AddWildcard,

    /// Add an explicit arm with the specified body.
    AddExplicitArm(String),

    /// Add a todo arm: `NewVariant => todo!()`
    AddTodo,

    /// Add unreachable arm: `NewVariant => unreachable!()`
    AddUnreachable,

    /// Prompt user for each match expression.
    Interactive,
}

/// Strategy for initializing new fields when adding to structs.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum FieldInitStrategy {
    /// Use `Default::default()` or `None` for Option types.
    #[default]
    UseDefault,

    /// Use a specific value.
    WithValue(String),

    /// Add the field as a new constructor parameter.
    AddToConstructor,

    /// Use a builder pattern.
    UseBuilder,

    /// Prompt user for each initialization site.
    Interactive,
}

/// Strategy for updating references during rename or removal.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum ReferenceUpdateStrategy {
    /// Update all references across the workspace.
    #[default]
    UpdateAll,

    /// Only update references within the same crate.
    SameCrateOnly,

    /// Only update references in specific files.
    SpecificFiles(Vec<PathBuf>),

    /// Don't update, just report what would change.
    ReportOnly,
}

/// Strategy for handling field/variant removal.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum RemovalStrategy {
    /// Remove all references (dangerous).
    RemoveAllReferences,

    /// Just report affected locations, don't modify.
    #[default]
    ReportOnly,

    /// Add deprecation warning first, remove later.
    DeprecateFirst {
        /// The deprecation warning message.
        warning_message: String,
    },

    /// Migrate to a replacement field/variant.
    MigrateTo {
        /// The replacement to use.
        replacement: String,
    },
}

/// Strategy for handling errors during cascade execution.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ErrorStrategy {
    /// Stop execution and report the error.
    #[default]
    StopAndReport,

    /// Log a warning and continue with remaining steps.
    ContinueWithWarning,

    /// Skip the failed step silently and continue.
    SkipAndContinue,

    /// Rollback all applied changes and report.
    Rollback,
}

/// Unified cascade strategy combining all individual strategies.
#[derive(Debug, Clone, Default)]
pub struct CascadeStrategy {
    /// Strategy for derive operations.
    pub derive: DeriveStrategy,

    /// Strategy for match expression updates.
    pub match_update: MatchUpdateStrategy,

    /// Strategy for field initialization.
    pub field_init: FieldInitStrategy,

    /// Strategy for reference updates.
    pub reference_update: ReferenceUpdateStrategy,

    /// Strategy for removal operations.
    pub removal: RemovalStrategy,

    /// Strategy for error handling.
    pub on_error: ErrorStrategy,
}

impl CascadeStrategy {
    /// Create a safe strategy that prioritizes data protection.
    ///
    /// - Deprecates before removing
    /// - Stops on errors
    /// - Reports rather than modifies for dangerous operations
    pub fn safe() -> Self {
        Self {
            derive: DeriveStrategy::TryAddDerive,
            match_update: MatchUpdateStrategy::AddTodo,
            field_init: FieldInitStrategy::UseDefault,
            reference_update: ReferenceUpdateStrategy::UpdateAll,
            removal: RemovalStrategy::DeprecateFirst {
                warning_message: "This item will be removed in a future version".to_string(),
            },
            on_error: ErrorStrategy::StopAndReport,
        }
    }

    /// Create an aggressive strategy that prioritizes automation.
    ///
    /// - Removes references directly
    /// - Continues past errors
    /// - Applies changes automatically
    pub fn aggressive() -> Self {
        Self {
            derive: DeriveStrategy::TryAddDerive,
            match_update: MatchUpdateStrategy::AddWildcard,
            field_init: FieldInitStrategy::UseDefault,
            reference_update: ReferenceUpdateStrategy::UpdateAll,
            removal: RemovalStrategy::RemoveAllReferences,
            on_error: ErrorStrategy::ContinueWithWarning,
        }
    }

    /// Create an interactive strategy that prompts for decisions.
    pub fn interactive() -> Self {
        Self {
            derive: DeriveStrategy::TryAddDerive,
            match_update: MatchUpdateStrategy::Interactive,
            field_init: FieldInitStrategy::Interactive,
            reference_update: ReferenceUpdateStrategy::UpdateAll,
            removal: RemovalStrategy::ReportOnly,
            on_error: ErrorStrategy::StopAndReport,
        }
    }

    /// Create an eager strategy that propagates changes recursively.
    pub fn eager() -> Self {
        Self::default()
    }

    /// Create a lazy strategy that only applies direct changes.
    pub fn lazy() -> Self {
        Self {
            derive: DeriveStrategy::SkipAndReport,
            ..Self::default()
        }
    }

    /// Check if this strategy uses eager propagation.
    pub fn is_eager(&self) -> bool {
        matches!(
            self.derive,
            DeriveStrategy::TryAddDerive | DeriveStrategy::GenerateImpl
        )
    }
}

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

    #[test]
    fn test_default_strategy() {
        let strategy = CascadeStrategy::default();
        assert_eq!(strategy.derive, DeriveStrategy::TryAddDerive);
        assert_eq!(strategy.match_update, MatchUpdateStrategy::AddWildcard);
        assert_eq!(strategy.on_error, ErrorStrategy::StopAndReport);
    }

    #[test]
    fn test_safe_strategy() {
        let strategy = CascadeStrategy::safe();
        assert!(matches!(
            strategy.removal,
            RemovalStrategy::DeprecateFirst { .. }
        ));
        assert_eq!(strategy.on_error, ErrorStrategy::StopAndReport);
    }

    #[test]
    fn test_aggressive_strategy() {
        let strategy = CascadeStrategy::aggressive();
        assert_eq!(strategy.removal, RemovalStrategy::RemoveAllReferences);
        assert_eq!(strategy.on_error, ErrorStrategy::ContinueWithWarning);
    }
}