1pub mod error;
8
9use std::{cmp, fmt, num::ParseIntError, ops, str};
10
11pub use error::ErrorCode;
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
16pub enum ExpectedVersion {
17 #[default]
19 Any,
20 Exists,
22 Empty,
24 Exact(u64),
26}
27
28impl ExpectedVersion {
29 pub fn from_next_version(version: u64) -> Self {
30 if version == 0 {
31 ExpectedVersion::Empty
32 } else {
33 ExpectedVersion::Exact(version - 1)
34 }
35 }
36
37 pub fn into_next_version(self) -> Option<u64> {
38 match self {
39 ExpectedVersion::Empty => Some(0),
40 ExpectedVersion::Exact(version) => version.checked_add(1),
41 _ => panic!("expected no stream or exact version"),
42 }
43 }
44
45 pub fn gap_from(self, current: CurrentVersion) -> VersionGap {
48 match (self, current) {
49 (ExpectedVersion::Any, _) => VersionGap::None,
51
52 (ExpectedVersion::Exists, CurrentVersion::Empty) => VersionGap::Incompatible,
54 (ExpectedVersion::Exists, CurrentVersion::Current(_)) => VersionGap::None,
55
56 (ExpectedVersion::Empty, CurrentVersion::Empty) => VersionGap::None,
58 (ExpectedVersion::Empty, CurrentVersion::Current(n)) => VersionGap::Ahead(n + 1),
59
60 (ExpectedVersion::Exact(expected), CurrentVersion::Empty) => {
62 VersionGap::Behind(expected + 1)
63 }
64 (ExpectedVersion::Exact(expected), CurrentVersion::Current(current)) => {
65 match expected.cmp(¤t) {
66 cmp::Ordering::Equal => VersionGap::None,
67 cmp::Ordering::Greater => VersionGap::Behind(expected - current),
68 cmp::Ordering::Less => VersionGap::Ahead(current - expected),
69 }
70 }
71 }
72 }
73
74 pub fn is_satisfied_by(self, current: CurrentVersion) -> bool {
76 matches!(self.gap_from(current), VersionGap::None)
77 }
78}
79
80impl fmt::Display for ExpectedVersion {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 ExpectedVersion::Any => write!(f, "any"),
84 ExpectedVersion::Exists => write!(f, "exists"),
85 ExpectedVersion::Empty => write!(f, "empty"),
86 ExpectedVersion::Exact(version) => version.fmt(f),
87 }
88 }
89}
90
91impl str::FromStr for ExpectedVersion {
92 type Err = ParseIntError;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 match s {
96 "empty" => Ok(ExpectedVersion::Empty),
97 "any" => Ok(ExpectedVersion::Any),
98 "exists" => Ok(ExpectedVersion::Exists),
99 num_str => {
100 let num = num_str.parse::<u64>()?;
101 Ok(ExpectedVersion::Exact(num))
102 }
103 }
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
108pub enum CurrentVersion {
110 Empty,
112 Current(u64),
114}
115
116impl CurrentVersion {
117 pub fn next(&self) -> u64 {
118 match self {
119 CurrentVersion::Current(version) => version + 1,
120 CurrentVersion::Empty => 0,
121 }
122 }
123
124 pub fn as_expected_version(&self) -> ExpectedVersion {
125 match self {
126 CurrentVersion::Current(version) => ExpectedVersion::Exact(*version),
127 CurrentVersion::Empty => ExpectedVersion::Empty,
128 }
129 }
130}
131
132impl fmt::Display for CurrentVersion {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 match self {
135 CurrentVersion::Current(version) => version.fmt(f),
136 CurrentVersion::Empty => write!(f, "empty"),
137 }
138 }
139}
140
141impl str::FromStr for CurrentVersion {
142 type Err = ParseIntError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 match s {
146 "empty" => Ok(CurrentVersion::Empty),
147 num_str => {
148 let num = num_str.parse::<u64>()?;
149 Ok(CurrentVersion::Current(num))
150 }
151 }
152 }
153}
154
155impl ops::AddAssign<u64> for CurrentVersion {
156 fn add_assign(&mut self, rhs: u64) {
157 match self {
158 CurrentVersion::Current(current) => *current += rhs,
159 CurrentVersion::Empty => {
160 if rhs > 0 {
161 *self = CurrentVersion::Current(rhs - 1)
162 }
163 }
164 }
165 }
166}
167
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169pub enum VersionGap {
170 None,
172 Ahead(u64),
174 Behind(u64),
176 Incompatible,
178}