use crate::{
db::cursor::{ContinuationSignature, CursorDecodeError, TokenWireError},
error::InternalError,
};
use icydb_diagnostic_code::{DiagnosticDecodeReason, DiagnosticFactTag};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CursorSignaturePrefix(u32);
impl CursorSignaturePrefix {
#[cfg(test)]
pub(crate) const UNKNOWN: Self = Self(0);
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]]))
}
const fn raw(self) -> u64 {
self.0 as u64
}
}
#[derive(Debug)]
pub enum CursorPlanError {
InvalidContinuationCursor { reason: CursorDecodeError },
InvalidContinuationCursorPayload {
reason: DiagnosticDecodeReason,
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: DiagnosticDecodeReason,
) -> Self {
Self::InvalidContinuationCursorPayload {
reason,
index: None,
}
}
pub(crate) const fn grouped_continuation_cursor_direction_mismatch() -> Self {
Self::invalid_continuation_cursor_payload(
DiagnosticDecodeReason::CursorGroupedDirectionMismatch,
)
}
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: &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(DiagnosticDecodeReason::CursorTokenEncode)
}
TokenWireError::Decode => {
Self::invalid_continuation_cursor_payload(DiagnosticDecodeReason::CursorTokenDecode)
}
}
}
#[must_use]
pub(crate) fn diagnostic_facts(&self) -> Vec<(DiagnosticFactTag, u64)> {
match self {
Self::InvalidContinuationCursor { reason } => match reason {
CursorDecodeError::Empty => vec![(
DiagnosticFactTag::DecodeReason,
DiagnosticDecodeReason::CursorEmpty.raw(),
)],
CursorDecodeError::TooLong { len, max } => vec![
(DiagnosticFactTag::ActualLength, *len as u64),
(DiagnosticFactTag::Maximum, *max as u64),
(
DiagnosticFactTag::DecodeReason,
DiagnosticDecodeReason::CursorTooLong.raw(),
),
],
CursorDecodeError::OddLength => vec![(
DiagnosticFactTag::DecodeReason,
DiagnosticDecodeReason::CursorOddLength.raw(),
)],
CursorDecodeError::InvalidHex { position } => vec![
(DiagnosticFactTag::ComponentIndex, *position as u64),
(
DiagnosticFactTag::DecodeReason,
DiagnosticDecodeReason::CursorInvalidHex.raw(),
),
],
},
Self::InvalidContinuationCursorPayload { reason, index } => {
let mut facts = Vec::with_capacity(usize::from(index.is_some()) + 1);
if let Some(index) = index {
facts.push((DiagnosticFactTag::ComponentIndex, *index as u64));
}
facts.push((DiagnosticFactTag::DecodeReason, reason.raw()));
facts
}
Self::ContinuationCursorInvariantViolation => Vec::new(),
Self::ContinuationCursorSignatureMismatch { expected, actual } => vec![
(DiagnosticFactTag::ExpectedSignaturePrefix, expected.raw()),
(DiagnosticFactTag::ActualSignaturePrefix, actual.raw()),
],
Self::ContinuationCursorWindowMismatch {
expected_offset,
actual_offset,
} => vec![
(
DiagnosticFactTag::ExpectedOffset,
u64::from(*expected_offset),
),
(DiagnosticFactTag::ActualOffset, u64::from(*actual_offset)),
],
}
}
pub(crate) fn into_internal_error(self) -> InternalError {
if self.is_invalid_continuation_cursor() {
InternalError::cursor_invalid_continuation()
} else {
InternalError::cursor_executor_invariant()
}
}
}