rusty_schema_diff/migration.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
//! Migration plan generation and management
//!
//! This module provides types and functionality for generating and managing
//! schema migration plans.
use serde::{Serialize, Deserialize};
use crate::analyzer::SchemaChange;
/// Represents a plan for migrating between schema versions
///
/// A migration plan contains all the necessary changes required to evolve
/// from one schema version to another, along with impact analysis.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrationPlan {
/// Version of the source schema
pub source_version: String,
/// Version of the target schema
pub target_version: String,
/// List of changes required for migration
pub changes: Vec<SchemaChange>,
/// Impact score (0-100) indicating the magnitude of changes
pub impact_score: u8,
/// Indicates whether this migration contains breaking changes
pub is_breaking: bool,
}
impl MigrationPlan {
/// Creates a new migration plan
///
/// # Arguments
/// * `source_version` - Version identifier of the source schema
/// * `target_version` - Version identifier of the target schema
/// * `changes` - List of schema changes required for migration
///
/// # Returns
/// A new MigrationPlan instance with calculated impact scores
pub fn new(source_version: String, target_version: String, changes: Vec<SchemaChange>) -> Self {
let impact_score = Self::calculate_impact(&changes);
let is_breaking = Self::detect_breaking_changes(&changes);
Self {
source_version,
target_version,
changes,
impact_score,
is_breaking,
}
}
/// Calculates the impact score of the migration
///
/// # Arguments
/// * `changes` - List of schema changes to analyze
///
/// # Returns
/// An impact score between 0 and 100
fn calculate_impact(changes: &[SchemaChange]) -> u8 {
// Simple scoring algorithm
let score = changes.iter().map(|change| match change.change_type {
crate::analyzer::ChangeType::Addition => 25,
crate::analyzer::ChangeType::Removal => 100,
crate::analyzer::ChangeType::Modification => 50,
crate::analyzer::ChangeType::Rename => 30,
}).max().unwrap_or(0);
score.min(100) as u8
}
/// Detects if the migration contains breaking changes
///
/// # Arguments
/// * `changes` - List of schema changes to analyze
///
/// # Returns
/// true if breaking changes are detected, false otherwise
fn detect_breaking_changes(changes: &[SchemaChange]) -> bool {
changes.iter().any(|change| matches!(
change.change_type,
crate::analyzer::ChangeType::Removal | crate::analyzer::ChangeType::Modification
))
}
/// Gets a list of breaking changes in the migration plan
///
/// # Returns
/// A vector of references to breaking changes
pub fn breaking_changes(&self) -> Vec<&SchemaChange> {
self.changes.iter()
.filter(|change| matches!(
change.change_type,
crate::analyzer::ChangeType::Removal | crate::analyzer::ChangeType::Modification
))
.collect()
}
}