use thiserror::Error;
pub type Result<T> = std::result::Result<T, OmniError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ManifestErrorKind {
BadRequest,
NotFound,
Conflict,
Internal,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ManifestConflictDetails {
ExpectedVersionMismatch {
table_key: String,
expected: u64,
actual: u64,
},
RowLevelCasContention,
}
#[derive(Debug, Clone, Error)]
#[error("{message}")]
pub struct ManifestError {
pub kind: ManifestErrorKind,
pub message: String,
pub details: Option<ManifestConflictDetails>,
}
impl ManifestError {
pub fn new(kind: ManifestErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
details: None,
}
}
pub fn with_details(mut self, details: ManifestConflictDetails) -> Self {
self.details = Some(details);
self
}
}
#[derive(Debug, Clone)]
pub struct MergeConflict {
pub table_key: String,
pub row_id: Option<String>,
pub kind: MergeConflictKind,
pub message: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeConflictKind {
DivergentInsert,
DivergentUpdate,
DeleteVsUpdate,
OrphanEdge,
UniqueViolation,
CardinalityViolation,
ValueConstraintViolation,
}
#[derive(Debug, Error)]
pub enum OmniError {
#[error("{0}")]
Compiler(#[from] omnigraph_compiler::error::NanoError),
#[error("storage: {0}")]
Lance(String),
#[error("query: {0}")]
DataFusion(String),
#[error("io: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Manifest(ManifestError),
#[error("merge conflicts: {0:?}")]
MergeConflicts(Vec<MergeConflict>),
}
impl OmniError {
pub fn manifest(message: impl Into<String>) -> Self {
Self::Manifest(ManifestError::new(ManifestErrorKind::BadRequest, message))
}
pub fn manifest_not_found(message: impl Into<String>) -> Self {
Self::Manifest(ManifestError::new(ManifestErrorKind::NotFound, message))
}
pub fn manifest_conflict(message: impl Into<String>) -> Self {
Self::Manifest(ManifestError::new(ManifestErrorKind::Conflict, message))
}
pub fn manifest_internal(message: impl Into<String>) -> Self {
Self::Manifest(ManifestError::new(ManifestErrorKind::Internal, message))
}
pub fn manifest_expected_version_mismatch(
table_key: impl Into<String>,
expected: u64,
actual: u64,
) -> Self {
let table_key = table_key.into();
let message = format!(
"stale view of '{}': expected manifest table version {} but current is {} — refresh and retry",
table_key, expected, actual
);
Self::Manifest(
ManifestError::new(ManifestErrorKind::Conflict, message).with_details(
ManifestConflictDetails::ExpectedVersionMismatch {
table_key,
expected,
actual,
},
),
)
}
pub fn manifest_row_level_cas_contention(message: impl Into<String>) -> Self {
Self::Manifest(
ManifestError::new(ManifestErrorKind::Conflict, message)
.with_details(ManifestConflictDetails::RowLevelCasContention),
)
}
}