1use std::num::NonZeroUsize;
4use std::time::Duration;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Quorum {
12 One,
14 Majority,
16 All,
18 N(NonZeroUsize),
20}
21
22impl Quorum {
23 #[must_use]
28 pub fn resolve(self, replication_factor: NonZeroUsize) -> usize {
29 let k = replication_factor.get();
30 match self {
31 Self::One => 1,
32 Self::Majority => k / 2 + 1,
33 Self::All => k,
34 Self::N(n) => n.get().min(k),
35 }
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct WriteOptions {
42 pub timeout: Duration,
44 pub quorum: Quorum,
46}
47
48impl WriteOptions {
49 pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
51
52 #[must_use]
54 pub const fn new() -> Self {
55 Self {
56 timeout: Self::DEFAULT_TIMEOUT,
57 quorum: Quorum::Majority,
58 }
59 }
60
61 #[must_use]
63 pub const fn with_timeout(mut self, timeout: Duration) -> Self {
64 self.timeout = timeout;
65 self
66 }
67
68 #[must_use]
70 pub const fn with_quorum(mut self, quorum: Quorum) -> Self {
71 self.quorum = quorum;
72 self
73 }
74}
75
76impl Default for WriteOptions {
77 fn default() -> Self {
80 Self::new()
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct ReadOptions {
87 pub timeout: Duration,
89 pub quorum: Quorum,
91 pub max_keys: usize,
97 pub cursor: Option<crate::Cursor>,
99}
100
101impl ReadOptions {
102 pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
104
105 pub const DEFAULT_MAX_KEYS: usize = 16;
107
108 #[must_use]
110 pub const fn new() -> Self {
111 Self {
112 timeout: Self::DEFAULT_TIMEOUT,
113 quorum: Quorum::One,
114 max_keys: Self::DEFAULT_MAX_KEYS,
115 cursor: None,
116 }
117 }
118
119 #[must_use]
121 pub const fn with_timeout(mut self, timeout: Duration) -> Self {
122 self.timeout = timeout;
123 self
124 }
125
126 #[must_use]
128 pub const fn with_quorum(mut self, quorum: Quorum) -> Self {
129 self.quorum = quorum;
130 self
131 }
132
133 #[must_use]
135 pub const fn with_max_keys(mut self, max_keys: usize) -> Self {
136 self.max_keys = max_keys;
137 self
138 }
139
140 #[must_use]
142 pub fn with_cursor(mut self, cursor: crate::Cursor) -> Self {
143 self.cursor = Some(cursor);
144 self
145 }
146}
147
148impl Default for ReadOptions {
149 fn default() -> Self {
153 Self::new()
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn k(n: usize) -> NonZeroUsize {
162 NonZeroUsize::new(n).unwrap()
163 }
164
165 #[test]
166 fn one_resolves_to_one() {
167 assert_eq!(Quorum::One.resolve(k(20)), 1);
168 }
169
170 #[test]
171 fn majority_is_strictly_more_than_half() {
172 assert_eq!(Quorum::Majority.resolve(k(20)), 11);
173 assert_eq!(Quorum::Majority.resolve(k(3)), 2);
174 assert_eq!(Quorum::Majority.resolve(k(1)), 1);
175 }
176
177 #[test]
178 fn all_resolves_to_the_replication_factor() {
179 assert_eq!(Quorum::All.resolve(k(20)), 20);
180 }
181
182 #[test]
183 fn explicit_n_is_honored_below_the_replication_factor() {
184 assert_eq!(Quorum::N(k(5)).resolve(k(20)), 5);
185 }
186
187 #[test]
188 fn explicit_n_clamps_to_the_replication_factor() {
189 assert_eq!(Quorum::N(k(100)).resolve(k(20)), 20);
192 }
193
194 #[test]
195 fn write_defaults_to_majority() {
196 assert_eq!(WriteOptions::default().quorum, Quorum::Majority);
197 assert_eq!(WriteOptions::default().timeout, Duration::from_secs(30));
198 }
199
200 #[test]
201 fn read_defaults_to_one() {
202 let opts = ReadOptions::default();
203 assert_eq!(opts.quorum, Quorum::One);
204 assert_eq!(opts.max_keys, 16);
205 assert!(opts.cursor.is_none());
206 }
207
208 #[test]
209 fn builders_override_defaults() {
210 let w = WriteOptions::new()
211 .with_timeout(Duration::from_secs(5))
212 .with_quorum(Quorum::All);
213 assert_eq!(w.timeout, Duration::from_secs(5));
214 assert_eq!(w.quorum, Quorum::All);
215
216 let r = ReadOptions::new()
217 .with_timeout(Duration::from_secs(1))
218 .with_quorum(Quorum::Majority)
219 .with_max_keys(4);
220 assert_eq!(r.timeout, Duration::from_secs(1));
221 assert_eq!(r.quorum, Quorum::Majority);
222 assert_eq!(r.max_keys, 4);
223 }
224}