1use core::fmt;
7
8#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct Revision(u64);
11
12impl Revision {
13 pub const ZERO: Self = Self(0);
15
16 pub const fn get(self) -> u64 {
18 self.0
19 }
20
21 pub fn advance(&mut self) -> Result<(), RevisionExhausted> {
23 let next = self.0.checked_add(1).ok_or(RevisionExhausted)?;
24 self.0 = next;
25 Ok(())
26 }
27}
28
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
31pub struct RevisionExhausted;
32
33impl fmt::Display for RevisionExhausted {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.write_str("revision exhausted")
36 }
37}
38
39#[cfg(feature = "std")]
40impl std::error::Error for RevisionExhausted {}
41
42#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct RevisionKey<const N: usize>([Revision; N]);
45
46impl<const N: usize> RevisionKey<N> {
47 pub const fn new(revisions: [Revision; N]) -> Self {
49 Self(revisions)
50 }
51
52 pub const fn as_array(&self) -> &[Revision; N] {
54 &self.0
55 }
56}
57
58impl<const N: usize> From<[Revision; N]> for RevisionKey<N> {
59 fn from(revisions: [Revision; N]) -> Self {
60 Self::new(revisions)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use alloc::string::ToString;
68
69 #[test]
70 fn revision_advances_from_zero() {
71 let mut revision = Revision::ZERO;
72 revision.advance().expect("advance");
73 assert_eq!(revision.get(), 1);
74 }
75
76 #[test]
77 fn exhausted_revision_is_unchanged() {
78 let mut revision = Revision(u64::MAX);
79 assert_eq!(revision.advance(), Err(RevisionExhausted));
80 assert_eq!(revision.get(), u64::MAX);
81 assert_eq!(RevisionExhausted.to_string(), "revision exhausted");
82 }
83
84 #[test]
85 fn key_round_trips_its_array() {
86 let key = RevisionKey::from([Revision::ZERO; 3]);
87 assert_eq!(key.as_array(), &[Revision::ZERO; 3]);
88 }
89}