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,
};
#[derive(Clone, Debug)]
pub struct MigrationCatalog {
bundle: VerifiedMigrationHistoryBundle,
graph: MigrationHistoryGraph,
context: Option<ManagedDeltaContext>,
}
impl MigrationCatalog {
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)
}
pub fn from_verified_bundle(
bundle: VerifiedMigrationHistoryBundle,
) -> Result<Self, Diagnostic> {
let graph = bundle.history_graph()?;
Ok(Self {
bundle,
graph,
context: None,
})
}
pub const fn fingerprint(&self) -> &Fingerprint {
self.bundle.fingerprint()
}
pub fn entries(&self) -> &[VerifiedMigrationHistoryBundleEntry] {
self.bundle.entries()
}
pub fn heads(&self) -> &[MigrationId] {
self.bundle.heads()
}
pub const fn graph(&self) -> &MigrationHistoryGraph {
&self.graph
}
pub const fn bundle(&self) -> &VerifiedMigrationHistoryBundle {
&self.bundle
}
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)
}
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,
)
}
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,
)
}
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,
)
}
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",
)
})
}
}