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}