1use std::fmt;
9use std::str::FromStr;
10
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct SuggestId {
35 index: u32,
36 generation: u32,
37}
38
39impl SuggestId {
40 pub fn new(index: u32, generation: u32) -> Self {
42 Self { index, generation }
43 }
44
45 #[allow(dead_code)]
47 pub(crate) fn from_raw(index: u32, generation: u32) -> Self {
48 Self { index, generation }
49 }
50
51 pub fn index(&self) -> u32 {
53 self.index
54 }
55
56 pub fn generation(&self) -> u32 {
58 self.generation
59 }
60
61 pub fn is_generation(&self, gen: u32) -> bool {
63 self.generation == gen
64 }
65
66 pub fn with_generation(&self, generation: u32) -> Self {
68 Self {
69 index: self.index,
70 generation,
71 }
72 }
73}
74
75impl fmt::Display for SuggestId {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(f, "S{:03}g{}", self.index, self.generation)
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum ParseSuggestIdError {
84 InvalidFormat,
86 InvalidIndex,
88 InvalidGeneration,
90}
91
92impl fmt::Display for ParseSuggestIdError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 Self::InvalidFormat => write!(f, "Invalid SuggestId format (expected S###g#)"),
96 Self::InvalidIndex => write!(f, "Invalid index in SuggestId"),
97 Self::InvalidGeneration => write!(f, "Invalid generation in SuggestId"),
98 }
99 }
100}
101
102impl std::error::Error for ParseSuggestIdError {}
103
104impl FromStr for SuggestId {
105 type Err = ParseSuggestIdError;
106
107 fn from_str(s: &str) -> Result<Self, Self::Err> {
108 let s = s.trim();
109
110 let rest = s
112 .strip_prefix('S')
113 .or_else(|| s.strip_prefix('s'))
114 .ok_or(ParseSuggestIdError::InvalidFormat)?;
115
116 let (index_str, gen_str) = rest
118 .split_once('g')
119 .or_else(|| rest.split_once('G'))
120 .ok_or(ParseSuggestIdError::InvalidFormat)?;
121
122 let index: u32 = index_str
123 .parse()
124 .map_err(|_| ParseSuggestIdError::InvalidIndex)?;
125 let generation: u32 = gen_str
126 .parse()
127 .map_err(|_| ParseSuggestIdError::InvalidGeneration)?;
128
129 Ok(SuggestId { index, generation })
130 }
131}
132
133impl Serialize for SuggestId {
135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136 where
137 S: Serializer,
138 {
139 serializer.serialize_str(&self.to_string())
140 }
141}
142
143impl<'de> Deserialize<'de> for SuggestId {
144 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
145 where
146 D: Deserializer<'de>,
147 {
148 let s = String::deserialize(deserializer)?;
149 s.parse().map_err(serde::de::Error::custom)
150 }
151}
152
153#[derive(Debug, Clone)]
155pub struct SuggestIdGenerator {
156 next_index: u32,
157}
158
159impl Default for SuggestIdGenerator {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl SuggestIdGenerator {
166 pub fn new() -> Self {
168 Self { next_index: 1 }
169 }
170
171 pub fn starting_at(index: u32) -> Self {
173 Self {
174 next_index: index.max(1),
175 }
176 }
177
178 pub fn next_id(&mut self) -> SuggestId {
180 let id = SuggestId::new(self.next_index, 0);
181 self.next_index += 1;
182 id
183 }
184
185 pub fn next_id_with_generation(&mut self, generation: u32) -> SuggestId {
187 let id = SuggestId::new(self.next_index, generation);
188 self.next_index += 1;
189 id
190 }
191
192 pub fn peek(&self) -> SuggestId {
194 SuggestId::new(self.next_index, 0)
195 }
196
197 pub fn reset(&mut self) {
199 self.next_index = 1;
200 }
201
202 pub fn current_index(&self) -> u32 {
204 self.next_index
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_suggest_id_new() {
214 let id = SuggestId::new(1, 0);
215 assert_eq!(id.index(), 1);
216 assert_eq!(id.generation(), 0);
217 }
218
219 #[test]
220 fn test_suggest_id_display() {
221 assert_eq!(SuggestId::new(1, 0).to_string(), "S001g0");
222 assert_eq!(SuggestId::new(42, 3).to_string(), "S042g3");
223 assert_eq!(SuggestId::new(999, 10).to_string(), "S999g10");
224 assert_eq!(SuggestId::new(1000, 0).to_string(), "S1000g0");
225 }
226
227 #[test]
228 fn test_suggest_id_parse() {
229 let id: SuggestId = "S001g0".parse().unwrap();
230 assert_eq!(id.index(), 1);
231 assert_eq!(id.generation(), 0);
232
233 let id2: SuggestId = "S042g3".parse().unwrap();
234 assert_eq!(id2.index(), 42);
235 assert_eq!(id2.generation(), 3);
236
237 let id3: SuggestId = "s123G5".parse().unwrap();
238 assert_eq!(id3.index(), 123);
239 assert_eq!(id3.generation(), 5);
240 }
241
242 #[test]
243 fn test_suggest_id_parse_errors() {
244 assert_eq!(
245 "001g0".parse::<SuggestId>().unwrap_err(),
246 ParseSuggestIdError::InvalidFormat
247 );
248 assert_eq!(
249 "S001".parse::<SuggestId>().unwrap_err(),
250 ParseSuggestIdError::InvalidFormat
251 );
252 assert_eq!(
253 "Sabcg0".parse::<SuggestId>().unwrap_err(),
254 ParseSuggestIdError::InvalidIndex
255 );
256 assert_eq!(
257 "S001gabc".parse::<SuggestId>().unwrap_err(),
258 ParseSuggestIdError::InvalidGeneration
259 );
260 }
261
262 #[test]
263 fn test_suggest_id_serde() {
264 let id = SuggestId::new(42, 3);
265 let json = serde_json::to_string(&id).unwrap();
266 assert_eq!(json, "\"S042g3\"");
267
268 let parsed: SuggestId = serde_json::from_str(&json).unwrap();
269 assert_eq!(parsed, id);
270 }
271
272 #[test]
273 fn test_suggest_id_ordering() {
274 let id1 = SuggestId::new(1, 0);
275 let id2 = SuggestId::new(2, 0);
276 let id3 = SuggestId::new(1, 1);
277
278 assert!(id1 < id2);
279 assert!(id1 < id3); let mut ids = vec![id2, id3, id1];
282 ids.sort();
283 assert_eq!(ids, vec![id1, id3, id2]);
284 }
285
286 #[test]
287 fn test_suggest_id_is_generation() {
288 let id = SuggestId::new(1, 3);
289 assert!(id.is_generation(3));
290 assert!(!id.is_generation(0));
291 assert!(!id.is_generation(4));
292 }
293
294 #[test]
295 fn test_suggest_id_with_generation() {
296 let id = SuggestId::new(42, 0);
297 let bumped = id.with_generation(5);
298 assert_eq!(bumped.index(), 42);
299 assert_eq!(bumped.generation(), 5);
300 }
301
302 #[test]
303 fn test_suggest_id_generator() {
304 let mut gen = SuggestIdGenerator::new();
305 assert_eq!(gen.next_id(), SuggestId::new(1, 0));
306 assert_eq!(gen.next_id(), SuggestId::new(2, 0));
307 assert_eq!(gen.next_id(), SuggestId::new(3, 0));
308 assert_eq!(gen.peek(), SuggestId::new(4, 0));
309 assert_eq!(gen.next_id(), SuggestId::new(4, 0));
310 }
311
312 #[test]
313 fn test_suggest_id_generator_with_generation() {
314 let mut gen = SuggestIdGenerator::new();
315 let id = gen.next_id_with_generation(5);
316 assert_eq!(id.index(), 1);
317 assert_eq!(id.generation(), 5);
318 }
319
320 #[test]
321 fn test_suggest_id_generator_starting_at() {
322 let mut gen = SuggestIdGenerator::starting_at(10);
323 assert_eq!(gen.next_id(), SuggestId::new(10, 0));
324 assert_eq!(gen.next_id(), SuggestId::new(11, 0));
325 }
326
327 #[test]
328 fn test_suggest_id_generator_reset() {
329 let mut gen = SuggestIdGenerator::new();
330 gen.next_id();
331 gen.next_id();
332 gen.reset();
333 assert_eq!(gen.next_id(), SuggestId::new(1, 0));
334 }
335}