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, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
108pub enum CurrentVersion {
110 #[default]
112 Empty,
113 Current(u64),
115}
116
117impl CurrentVersion {
118 pub fn next(&self) -> u64 {
119 match self {
120 CurrentVersion::Current(version) => version + 1,
121 CurrentVersion::Empty => 0,
122 }
123 }
124
125 pub fn as_expected_version(&self) -> ExpectedVersion {
126 match self {
127 CurrentVersion::Current(version) => ExpectedVersion::Exact(*version),
128 CurrentVersion::Empty => ExpectedVersion::Empty,
129 }
130 }
131}
132
133impl fmt::Display for CurrentVersion {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self {
136 CurrentVersion::Current(version) => version.fmt(f),
137 CurrentVersion::Empty => write!(f, "empty"),
138 }
139 }
140}
141
142impl str::FromStr for CurrentVersion {
143 type Err = ParseIntError;
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 match s {
147 "empty" => Ok(CurrentVersion::Empty),
148 num_str => {
149 let num = num_str.parse::<u64>()?;
150 Ok(CurrentVersion::Current(num))
151 }
152 }
153 }
154}
155
156impl ops::AddAssign<u64> for CurrentVersion {
157 fn add_assign(&mut self, rhs: u64) {
158 match self {
159 CurrentVersion::Current(current) => *current += rhs,
160 CurrentVersion::Empty => {
161 if rhs > 0 {
162 *self = CurrentVersion::Current(rhs - 1)
163 }
164 }
165 }
166 }
167}
168
169#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
170pub enum VersionGap {
171 #[default]
173 None,
174 Ahead(u64),
176 Behind(u64),
178 Incompatible,
180}