use crate::{
db::cursor::{ContinuationSignature, CursorDecodeError, TokenWireError},
error::InternalError,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CursorPayloadErrorCode(u8);
impl CursorPayloadErrorCode {
pub(crate) const GROUPED_DIRECTION_MISMATCH: Self = Self(2);
pub(crate) const TOKEN_ENCODE: Self = Self(18);
pub(crate) const TOKEN_DECODE: Self = Self(19);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CursorSignaturePrefix(u32);
impl CursorSignaturePrefix {
#[cfg(test)]
pub(crate) const UNKNOWN: Self = Self(0);
pub(crate) const fn from_signature(signature: &ContinuationSignature) -> Self {
let bytes = (*signature).into_bytes();
Self(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
}
#[derive(Debug)]
pub enum CursorPlanError {
InvalidContinuationCursor { reason: CursorDecodeError },
InvalidContinuationCursorPayload {
reason: CursorPayloadErrorCode,
index: Option<usize>,
},
ContinuationCursorInvariantViolation,
ContinuationCursorSignatureMismatch {
expected: CursorSignaturePrefix,
actual: CursorSignaturePrefix,
},
ContinuationCursorWindowMismatch {
expected_offset: u32,
actual_offset: u32,
},
}
impl CursorPlanError {
#[must_use]
pub(crate) const fn is_invalid_continuation_cursor(&self) -> bool {
match self {
Self::InvalidContinuationCursor { .. }
| Self::InvalidContinuationCursorPayload { .. }
| Self::ContinuationCursorSignatureMismatch { .. }
| Self::ContinuationCursorWindowMismatch { .. } => true,
Self::ContinuationCursorInvariantViolation => false,
}
}
pub(in crate::db) const fn invalid_continuation_cursor(reason: CursorDecodeError) -> Self {
Self::InvalidContinuationCursor { reason }
}
pub(in crate::db) const fn invalid_continuation_cursor_payload(
reason: CursorPayloadErrorCode,
) -> Self {
Self::InvalidContinuationCursorPayload {
reason,
index: None,
}
}
pub(crate) const fn grouped_continuation_cursor_direction_mismatch() -> Self {
Self::invalid_continuation_cursor_payload(
CursorPayloadErrorCode::GROUPED_DIRECTION_MISMATCH,
)
}
pub(in crate::db) const fn continuation_cursor_invariant() -> Self {
Self::ContinuationCursorInvariantViolation
}
pub(in crate::db) const fn cursor_requires_non_empty_order() -> Self {
Self::continuation_cursor_invariant()
}
pub(in crate::db) const fn continuation_cursor_signature_mismatch(
_entity_path: &'static str,
expected: &ContinuationSignature,
actual: &ContinuationSignature,
) -> Self {
Self::ContinuationCursorSignatureMismatch {
expected: CursorSignaturePrefix::from_signature(expected),
actual: CursorSignaturePrefix::from_signature(actual),
}
}
pub(in crate::db) const fn continuation_cursor_window_mismatch(
expected_offset: u32,
actual_offset: u32,
) -> Self {
Self::ContinuationCursorWindowMismatch {
expected_offset,
actual_offset,
}
}
pub(in crate::db) const fn from_token_wire_error(err: TokenWireError) -> Self {
match err {
TokenWireError::Encode => {
Self::invalid_continuation_cursor_payload(CursorPayloadErrorCode::TOKEN_ENCODE)
}
TokenWireError::Decode => {
Self::invalid_continuation_cursor_payload(CursorPayloadErrorCode::TOKEN_DECODE)
}
}
}
pub(crate) fn into_internal_error(self) -> InternalError {
if self.is_invalid_continuation_cursor() {
InternalError::cursor_invalid_continuation()
} else {
InternalError::cursor_executor_invariant()
}
}
}