use super::canonical::{canonical_digest, validate_envelope};
use super::error::ReplicationError;
use super::types::{same_identity, MemoryMutationEnvelopeV1};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplicaState {
Bootstrapping,
InstallingSnapshot,
CatchingUp,
Current,
Lagging,
OfflineUsable,
AwaitingGap,
GapDetected,
Quarantined,
Rejected,
Retired,
}
impl ReplicaState {
pub fn can_transition_to(self, next: Self) -> bool {
use ReplicaState::*;
matches!(
(self, next),
(Rejected, Rejected) | (Retired, Retired)
| (Bootstrapping, InstallingSnapshot | Quarantined | Rejected | Retired)
| (InstallingSnapshot, CatchingUp | Quarantined | Rejected | Retired)
| (CatchingUp, Current | AwaitingGap | Quarantined | Retired)
| (
Current,
Lagging | OfflineUsable | InstallingSnapshot | Quarantined | Retired
)
| (
Lagging | OfflineUsable,
Current | AwaitingGap | InstallingSnapshot | Quarantined | Retired
)
| (AwaitingGap, CatchingUp | GapDetected | Quarantined | Retired)
| (GapDetected, InstallingSnapshot | Quarantined | Rejected | Retired)
| (Quarantined, InstallingSnapshot | Rejected | Retired)
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReplicaWatermarkV1 {
pub sequence: u64,
pub head_digest: [u8; 32],
}
impl ReplicaWatermarkV1 {
pub fn new(sequence: u64, head_digest: [u8; 32]) -> Self {
Self {
sequence,
head_digest,
}
}
pub fn accepts_next(&self, sequence: u64, previous: [u8; 32]) -> bool {
if sequence <= self.sequence {
return false;
}
match self.sequence.checked_add(1) {
Some(next) if sequence == next => {}
_ => return false,
}
previous == self.head_digest
}
}
pub fn validate_identity_collision(
first: &MemoryMutationEnvelopeV1,
second: &MemoryMutationEnvelopeV1,
) -> Result<(), ReplicationError> {
validate_envelope(first)
.map_err(|e| ReplicationError::PreCollisionValidation(format!("first envelope: {e}")))?;
validate_envelope(second)
.map_err(|e| ReplicationError::PreCollisionValidation(format!("second envelope: {e}")))?;
if !same_identity(first, second) {
return Ok(());
}
let first_digest = canonical_digest(first)?;
let second_digest = canonical_digest(second)?;
if first_digest == second_digest {
Ok(())
} else {
Err(ReplicationError::IdentityCollision)
}
}