type-bridge-schema-migration 2.2.2

Canonical schema migration planning for type-bridge
Documentation
//! Owned, source-free runtime catalog over a verified history bundle.

use std::collections::BTreeSet;

use type_bridge_contract::diagnostic::Diagnostic;
use type_bridge_contract::diagnostic::{DiagnosticCategory, DiagnosticCode};
use type_bridge_contract::fingerprint::Fingerprint;
use type_bridge_contract::migration::MigrationId;
use type_bridge_schema::ManagedDeltaContext;

use crate::{
    MigrationApplyApproval, MigrationApplyPlanError, MigrationApplyTarget, MigrationHistoryGraph,
    MigrationSafetyPolicy, SchemaLoweringBinding, VerifiedMigrationApplyPlan,
    VerifiedMigrationHistoryBundle, VerifiedMigrationHistoryBundleEntry,
    VerifiedMigrationRollbackPlan, build_verified_migration_apply_plan,
    build_verified_migration_apply_preview, build_verified_migration_rollback_plan,
    build_verified_migration_rollback_preview, decode_verified_migration_history_bundle,
};

/// Immutable runtime migration catalog admitted from canonical generated bytes.
#[derive(Clone, Debug)]
pub struct MigrationCatalog {
    bundle: VerifiedMigrationHistoryBundle,
    graph: MigrationHistoryGraph,
    context: Option<ManagedDeltaContext>,
}

impl MigrationCatalog {
    /// Decode, replay, and open one generated canonical history bundle.
    pub fn open(bytes: &[u8], context: &ManagedDeltaContext) -> Result<Self, Diagnostic> {
        let mut catalog =
            Self::from_verified_bundle(decode_verified_migration_history_bundle(bytes, context)?)?;
        catalog.context = Some(context.clone());
        Ok(catalog)
    }

    /// Open one already verified immutable bundle.
    pub fn from_verified_bundle(
        bundle: VerifiedMigrationHistoryBundle,
    ) -> Result<Self, Diagnostic> {
        let graph = bundle.history_graph()?;
        Ok(Self {
            bundle,
            graph,
            context: None,
        })
    }

    /// Return the exact canonical bundle content identity.
    pub const fn fingerprint(&self) -> &Fingerprint {
        self.bundle.fingerprint()
    }

    /// Return history entries in deterministic topological order.
    pub fn entries(&self) -> &[VerifiedMigrationHistoryBundleEntry] {
        self.bundle.entries()
    }

    /// Return canonical graph heads.
    pub fn heads(&self) -> &[MigrationId] {
        self.bundle.heads()
    }

    /// Borrow the replay-verified graph used by preview and execution planning.
    pub const fn graph(&self) -> &MigrationHistoryGraph {
        &self.graph
    }

    /// Borrow the complete verified bundle authority.
    pub const fn bundle(&self) -> &VerifiedMigrationHistoryBundle {
        &self.bundle
    }

    /// Build a fully replayed provider-free forward preview under this catalog's authority.
    pub fn preview_apply(
        &self,
        applied: &BTreeSet<MigrationId>,
        target: &MigrationApplyTarget,
    ) -> Result<VerifiedMigrationApplyPlan, MigrationApplyPlanError> {
        let context = self.execution_context()?;
        let lowering = SchemaLoweringBinding::current(context.available_capabilities().clone())?;
        build_verified_migration_apply_preview(&self.graph, applied, target, context, &lowering)
    }

    /// Build a fully replayed provider-free rollback preview under this catalog's authority.
    pub fn preview_rollback(
        &self,
        applied: &BTreeSet<MigrationId>,
        removals: &BTreeSet<MigrationId>,
    ) -> Result<VerifiedMigrationRollbackPlan, MigrationApplyPlanError> {
        let context = self.execution_context()?;
        let lowering = SchemaLoweringBinding::current(context.available_capabilities().clone())?;
        build_verified_migration_rollback_preview(
            &self.graph,
            applied,
            removals,
            context,
            &lowering,
        )
    }

    /// Build an executable forward plan from exact policy and approval authority.
    pub fn authorize_apply(
        &self,
        applied: &BTreeSet<MigrationId>,
        target: &MigrationApplyTarget,
        policy: &MigrationSafetyPolicy,
        approvals: &[MigrationApplyApproval],
    ) -> Result<VerifiedMigrationApplyPlan, MigrationApplyPlanError> {
        let context = self.execution_context()?;
        let lowering = SchemaLoweringBinding::current(context.available_capabilities().clone())?;
        build_verified_migration_apply_plan(
            &self.graph,
            applied,
            target,
            context,
            &lowering,
            policy,
            approvals,
        )
    }

    /// Build an executable rollback plan from exact policy and approval authority.
    pub fn authorize_rollback(
        &self,
        applied: &BTreeSet<MigrationId>,
        removals: &BTreeSet<MigrationId>,
        policy: &MigrationSafetyPolicy,
        approvals: &[MigrationApplyApproval],
    ) -> Result<VerifiedMigrationRollbackPlan, MigrationApplyPlanError> {
        let context = self.execution_context()?;
        let lowering = SchemaLoweringBinding::current(context.available_capabilities().clone())?;
        build_verified_migration_rollback_plan(
            &self.graph,
            applied,
            removals,
            context,
            &lowering,
            policy,
            approvals,
        )
    }

    /// Return the verified runtime context retained when opening generated bytes.
    pub fn execution_context(&self) -> Result<&ManagedDeltaContext, Diagnostic> {
        self.context.as_ref().ok_or_else(|| {
            Diagnostic::new(
                DiagnosticCategory::InvalidContract,
                DiagnosticCode::new("migration_catalog_runtime_context_required")
                    .expect("static catalog diagnostic code"),
                "migration planning requires a catalog opened under generated authority",
            )
        })
    }
}