use thiserror::Error;
pub const RESYNC_REQUIRED_STATUS: u16 = 409;
pub const RESYNC_REQUIRED_CODE: &str = "resync_required";
pub const HORIZON_REVISION_DETAIL: &str = "horizonRevision";
pub const FULL_RESYNC_CURSOR: i64 = 0;
#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum PullCursorError {
#[error("pull cursor must not be negative")]
Negative,
#[error("pull cursor is below the purge horizon {horizon_revision}")]
ResyncRequired {
horizon_revision: i64,
},
}
pub const fn check_pull_cursor(cursor: i64, horizon_revision: i64) -> Result<(), PullCursorError> {
if cursor < FULL_RESYNC_CURSOR {
return Err(PullCursorError::Negative);
}
if cursor > FULL_RESYNC_CURSOR && cursor < horizon_revision {
return Err(PullCursorError::ResyncRequired { horizon_revision });
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cursor_zero_is_accepted_at_every_horizon() {
assert_eq!(check_pull_cursor(0, 0), Ok(()));
assert_eq!(check_pull_cursor(0, i64::MAX), Ok(()));
}
#[test]
fn cursor_at_or_above_the_horizon_is_incremental() {
assert_eq!(check_pull_cursor(10, 10), Ok(()));
assert_eq!(check_pull_cursor(11, 10), Ok(()));
assert_eq!(check_pull_cursor(1, 0), Ok(()));
}
#[test]
fn cursor_below_the_horizon_requires_resync() {
assert_eq!(
check_pull_cursor(9, 10),
Err(PullCursorError::ResyncRequired {
horizon_revision: 10
})
);
assert_eq!(
check_pull_cursor(1, 2),
Err(PullCursorError::ResyncRequired {
horizon_revision: 2
})
);
}
#[test]
fn horizon_detail_uses_the_camel_case_wire_name() {
assert_eq!(HORIZON_REVISION_DETAIL, "horizonRevision");
}
#[test]
fn negative_cursor_is_rejected() {
assert_eq!(check_pull_cursor(-1, 0), Err(PullCursorError::Negative));
}
}