use core::fmt;
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Revision(u64);
impl Revision {
pub const ZERO: Self = Self(0);
pub const fn get(self) -> u64 {
self.0
}
pub fn advance(&mut self) -> Result<(), RevisionExhausted> {
let next = self.0.checked_add(1).ok_or(RevisionExhausted)?;
self.0 = next;
Ok(())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct RevisionExhausted;
impl fmt::Display for RevisionExhausted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("revision exhausted")
}
}
#[cfg(feature = "std")]
impl std::error::Error for RevisionExhausted {}
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RevisionKey<const N: usize>([Revision; N]);
impl<const N: usize> RevisionKey<N> {
pub const fn new(revisions: [Revision; N]) -> Self {
Self(revisions)
}
pub const fn as_array(&self) -> &[Revision; N] {
&self.0
}
}
impl<const N: usize> From<[Revision; N]> for RevisionKey<N> {
fn from(revisions: [Revision; N]) -> Self {
Self::new(revisions)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn revision_advances_from_zero() {
let mut revision = Revision::ZERO;
revision.advance().expect("advance");
assert_eq!(revision.get(), 1);
}
#[test]
fn exhausted_revision_is_unchanged() {
let mut revision = Revision(u64::MAX);
assert_eq!(revision.advance(), Err(RevisionExhausted));
assert_eq!(revision.get(), u64::MAX);
assert_eq!(RevisionExhausted.to_string(), "revision exhausted");
}
#[test]
fn key_round_trips_its_array() {
let key = RevisionKey::from([Revision::ZERO; 3]);
assert_eq!(key.as_array(), &[Revision::ZERO; 3]);
}
}