use crate::{
db::{
Db,
migration::{self, MigrationRunOutcome, MigrationRunState},
schema_evolution::{MigrationRegistry, SchemaMigrationDescriptor, SchemaMigrationPlanner},
},
error::InternalError,
traits::CanisterKind,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SchemaMigrationExecutionOutcome {
AlreadyApplied,
Executed(MigrationRunOutcome),
}
impl SchemaMigrationExecutionOutcome {
#[must_use]
pub const fn already_applied(self) -> bool {
matches!(self, Self::AlreadyApplied)
}
#[must_use]
pub const fn migration_outcome(self) -> Option<MigrationRunOutcome> {
match self {
Self::AlreadyApplied => None,
Self::Executed(outcome) => Some(outcome),
}
}
}
pub(in crate::db) fn execute_schema_migration_descriptor<C: CanisterKind>(
db: &Db<C>,
registry: &mut MigrationRegistry,
planner: &SchemaMigrationPlanner,
descriptor: &SchemaMigrationDescriptor,
max_steps: usize,
) -> Result<SchemaMigrationExecutionOutcome, InternalError> {
if registry.is_applied(descriptor.migration_id(), descriptor.version()) {
return Ok(SchemaMigrationExecutionOutcome::AlreadyApplied);
}
let plan = planner.plan(descriptor)?;
let outcome = migration::execute_migration_plan(db, &plan, max_steps)?;
if matches!(outcome.state(), MigrationRunState::Complete) {
registry.record_applied(descriptor.migration_id(), descriptor.version());
}
Ok(SchemaMigrationExecutionOutcome::Executed(outcome))
}