use serde::{Deserialize, Serialize};
use crate::solve::build::BuildError;
use crate::solve::mcsplit::IsoError;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum MigError {
#[error("existence check failed: {0}")]
Existence(#[from] ExistenceError),
#[error("lift failed: {0}")]
Lift(#[from] LiftError),
#[error("compose failed: {0}")]
Compose(#[from] ComposeError),
#[error("inversion failed: {0}")]
Invert(#[from] InvertError),
#[error("span search failed: {0}")]
Span(#[from] SpanError),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SpanError {
#[error("the search network could not be built: {source}")]
Build {
#[from]
source: BuildError,
},
#[error("the apex is not a well-formed sub-schema of the source: {source}")]
Apex {
#[from]
source: panproto_schema::SchemaError,
},
#[error(
"the total-morphism search stopped on {limit:?} before reaching any complete \
assignment, so whether a total morphism exists is unknown"
)]
Stopped {
limit: crate::solve::LimitKind,
},
#[error("the maximum common sub-schema search refused the network: {source}")]
Iso {
#[from]
source: IsoError,
},
#[error(
"`epic` asks for a surjective vertex map, which is a property of a total \
morphism rather than of a span; use `find_morphisms` or \
`find_best_morphism` for a surjective total morphism"
)]
EpicIsNotASpanProperty,
#[error(
"the span's right leg identifies two apex vertices, so merging along it \
would not commute; search with `iso` for a span that embeds"
)]
ContractingRightLeg,
}
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
#[non_exhaustive]
pub enum ExistenceError {
#[error("edge missing: {src} -> {tgt} (kind: {kind})")]
EdgeMissing {
src: String,
tgt: String,
kind: String,
},
#[error("kind inconsistency for {kind}: targets = {targets:?}")]
KindInconsistency {
kind: String,
targets: Vec<String>,
},
#[error("label inconsistency for {label}: targets = {targets:?}")]
LabelInconsistency {
label: String,
targets: Vec<String>,
},
#[error("required field missing: vertex {vertex}, field {field}")]
RequiredFieldMissing {
vertex: String,
field: String,
},
#[error("constraint tightened on {vertex}: {sort} changed from {src_val} to {tgt_val}")]
ConstraintTightened {
vertex: String,
sort: String,
src_val: String,
tgt_val: String,
},
#[error("resolver invalid for pair ({}, {})", pair.0, pair.1)]
ResolverInvalid {
pair: (String, String),
},
#[error("well-formedness: {message}")]
WellFormedness {
message: String,
},
#[error("signature incoherent for hyper-edge {hyper_edge}: label {label}")]
SignatureCoherence {
hyper_edge: String,
label: String,
},
#[error("simultaneity violation for hyper-edge {hyper_edge}: missing label {missing_label}")]
Simultaneity {
hyper_edge: String,
missing_label: String,
},
#[error("reachability risk for vertex {vertex}: {reason}")]
ReachabilityRisk {
vertex: String,
reason: String,
},
#[error("migration is not a theory morphism on its mapped fragment: {detail}")]
NotAMorphism {
detail: String,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum LiftError {
#[error("restrict failed: {0}")]
Restrict(#[from] panproto_inst::RestrictError),
#[error("target schema is required for W-type lift")]
MissingTargetSchema,
#[error("chase failed: {0}")]
Chase(#[from] crate::chase::ChaseError),
#[error(
"term-level chase did not terminate within {max_iterations} iterations / {max_nulls} nulls"
)]
ChaseBudgetExhausted {
max_iterations: usize,
max_nulls: usize,
},
}
impl LiftError {
#[must_use]
pub const fn is_retryable(&self) -> bool {
match self {
Self::Chase(err) => err.is_retryable(),
Self::ChaseBudgetExhausted { .. } => true,
_ => false,
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ComposeError {
#[error("edge not found in second migration's domain: {src} -> {tgt} ({kind})")]
EdgeNotInDomain {
src: String,
tgt: String,
kind: String,
},
#[error(
"migrations are not composable: first codomain `{first_codomain}` != second domain `{second_domain}`"
)]
DomainMismatch {
first_codomain: String,
second_domain: String,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum InvertError {
#[error("vertex map is not bijective: {detail}")]
NotBijective {
detail: String,
},
#[error("edge map is not bijective: {detail}")]
EdgeNotBijective {
detail: String,
},
#[error("migration drops vertices: {dropped:?}")]
DroppedVertices {
dropped: Vec<String>,
},
#[error("migration drops edges")]
DroppedEdges,
#[error("hyper-edge map is not bijective: {detail}")]
HyperEdgeNotBijective {
detail: String,
},
#[error("migration drops hyper-edges: {dropped:?}")]
DroppedHyperEdges {
dropped: Vec<String>,
},
#[error(
"the coercion at vertex `{vertex}` records no inverse term, so the values \
it rewrites cannot be brought back; the inverse migration is undefined \
there"
)]
CoercionNotInvertible {
vertex: String,
},
}