Skip to main content

blockworx/doc_ng/
rev.rs

1//! The server-assigned commit sequence number — the first component of the
2//! total write order. Totality is structural: the head rev lives in the
3//! document and is minted only by a successful `try_apply`, so a refused
4//! commit consumes nothing and the log cannot gap. A `Rev` outside the
5//! fold is inert (`try_apply` takes no rev), so `next` stays public for
6//! the sync layer's gap checks while minting stays module-private.
7//!
8//! A head is one of two *kinds* of position, as different types so a
9//! prediction cannot be mistaken for the authority's copy: [`Confirmed`]
10//! names a real log position, [`Provisional`] names scratch.
11
12use serde::{Deserialize, Serialize};
13
14#[derive(
15    Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Serialize, Deserialize,
16)]
17pub struct Rev(u64);
18
19impl Rev {
20    /// The empty document: no commit accepted yet. Real revs start at 1,
21    /// so ZERO sorts below every accepted write.
22    pub const ZERO: Rev = Rev(0);
23
24    /// Test fixtures only: production revs enter a process exclusively via
25    /// `Document::try_apply` (`next`) or the wire (`Deserialize`).
26    #[cfg(test)]
27    pub(in crate::doc_ng) const fn new(n: u64) -> Self {
28        Self(n)
29    }
30
31    pub const fn get(self) -> u64 {
32        self.0
33    }
34
35    #[must_use]
36    pub const fn next(self) -> Self {
37        Self(self.0 + 1)
38    }
39}
40
41/// Both kinds mint plain [`Rev`]s into write orders: a provisional write
42/// and a confirmed one must stay *mutually comparable*, because that
43/// comparison is what makes an unacknowledged local value outrank an
44/// incoming confirmed one. The typing stops at the document boundary;
45/// `WriteOrder` and `Register` never see it.
46pub trait RevKind: Copy + Ord + Default {
47    #[must_use]
48    fn next(self) -> Self;
49    fn minting(self) -> Rev;
50}
51
52/// A real position in the server's log.
53#[derive(
54    Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Serialize, Deserialize,
55)]
56pub struct Confirmed(Rev);
57
58impl Confirmed {
59    pub const fn get(self) -> Rev {
60        self.0
61    }
62}
63
64impl RevKind for Confirmed {
65    fn next(self) -> Self {
66        Self(self.0.next())
67    }
68    fn minting(self) -> Rev {
69        self.0
70    }
71}
72
73/// Scratch: every rebuild re-mints it from the confirmed head, so it
74/// names no log position — hence no accessor and no `Deserialize`.
75#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
76pub struct Provisional(Rev);
77
78impl Provisional {
79    pub(in crate::doc_ng) const fn new(head: Rev) -> Self {
80        Self(head)
81    }
82}
83
84impl RevKind for Provisional {
85    fn next(self) -> Self {
86        Self(self.0.next())
87    }
88    fn minting(self) -> Rev {
89        self.0
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    /// Both kinds mint the same plain rev, so a provisional write order
98    /// and a confirmed one compare — the suppression rule depends on it.
99    #[test]
100    fn the_two_kinds_mint_into_one_order() {
101        let head = Rev::ZERO.next();
102        assert_eq!(
103            Confirmed::default().next().minting(),
104            Provisional::new(Rev::ZERO).next().minting(),
105        );
106        assert!(Provisional::new(head).next().minting() > Confirmed(head).minting());
107    }
108
109    #[test]
110    fn next_advances_and_orders() {
111        let one = Rev::ZERO.next();
112        assert_eq!(one, Rev::new(1));
113        assert!(Rev::ZERO < one);
114    }
115}