1use crate::segment::OwnedSegment;
7
8#[derive(Debug)]
10pub struct SegmentNotFound {
11 pub expected: String,
12}
13
14impl std::fmt::Display for SegmentNotFound {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 write!(f, "Expected segment '{}' not found", self.expected)
17 }
18}
19
20impl std::error::Error for SegmentNotFound {}
21
22pub struct SegmentCursor {
27 position: usize,
28 total: usize,
29}
30
31impl SegmentCursor {
32 pub fn new(total: usize) -> Self {
33 Self { position: 0, total }
34 }
35
36 pub fn position(&self) -> usize {
38 self.position
39 }
40
41 pub fn remaining(&self) -> usize {
43 self.total.saturating_sub(self.position)
44 }
45
46 pub fn is_exhausted(&self) -> bool {
48 self.position >= self.total
49 }
50
51 pub fn advance(&mut self) {
53 self.position += 1;
54 }
55
56 pub fn save(&self) -> usize {
58 self.position
59 }
60
61 pub fn restore(&mut self, saved: usize) {
63 self.position = saved;
64 }
65}
66
67pub fn peek_is(segments: &[OwnedSegment], cursor: &SegmentCursor, tag: &str) -> bool {
69 if cursor.is_exhausted() {
70 return false;
71 }
72 segments[cursor.position()].is(tag)
73}
74
75pub fn consume<'a>(
78 segments: &'a [OwnedSegment],
79 cursor: &mut SegmentCursor,
80) -> Option<&'a OwnedSegment> {
81 if cursor.is_exhausted() {
82 return None;
83 }
84 let seg = &segments[cursor.position()];
85 cursor.advance();
86 Some(seg)
87}
88
89pub fn expect_segment<'a>(
92 segments: &'a [OwnedSegment],
93 cursor: &mut SegmentCursor,
94 tag: &str,
95) -> Result<&'a OwnedSegment, SegmentNotFound> {
96 if cursor.is_exhausted() {
97 return Err(SegmentNotFound {
98 expected: tag.to_string(),
99 });
100 }
101 let seg = &segments[cursor.position()];
102 if !seg.is(tag) {
103 return Err(SegmentNotFound {
104 expected: tag.to_string(),
105 });
106 }
107 cursor.advance();
108 Ok(seg)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn make_segment(id: &str) -> OwnedSegment {
116 OwnedSegment {
117 id: id.to_string(),
118 elements: vec![],
119 segment_number: 0,
120 }
121 }
122
123 #[test]
124 fn test_cursor_peek_and_advance() {
125 let mut cursor = SegmentCursor::new(4);
126
127 assert_eq!(cursor.position(), 0);
128 assert!(!cursor.is_exhausted());
129
130 cursor.advance();
131 assert_eq!(cursor.position(), 1);
132
133 cursor.advance();
134 cursor.advance();
135 cursor.advance();
136 assert!(cursor.is_exhausted());
137 }
138
139 #[test]
140 fn test_cursor_remaining() {
141 let mut cursor = SegmentCursor::new(5);
142 assert_eq!(cursor.remaining(), 5);
143 cursor.advance();
144 assert_eq!(cursor.remaining(), 4);
145 }
146
147 #[test]
148 fn test_cursor_save_restore() {
149 let mut cursor = SegmentCursor::new(10);
150 cursor.advance();
151 cursor.advance();
152
153 let saved = cursor.save();
154 assert_eq!(saved, 2);
155
156 cursor.advance();
157 cursor.advance();
158 assert_eq!(cursor.position(), 4);
159
160 cursor.restore(saved);
161 assert_eq!(cursor.position(), 2);
162 }
163
164 #[test]
165 fn test_cursor_empty() {
166 let cursor = SegmentCursor::new(0);
167 assert!(cursor.is_exhausted());
168 assert_eq!(cursor.remaining(), 0);
169 }
170
171 #[test]
172 fn test_peek_is_helper() {
173 let segments = vec![make_segment("NAD"), make_segment("IDE")];
174 let cursor = SegmentCursor::new(segments.len());
175 assert!(peek_is(&segments, &cursor, "NAD"));
176 assert!(!peek_is(&segments, &cursor, "IDE"));
177 }
178
179 #[test]
180 fn test_peek_is_exhausted() {
181 let segments: Vec<OwnedSegment> = vec![];
182 let cursor = SegmentCursor::new(0);
183 assert!(!peek_is(&segments, &cursor, "NAD"));
184 }
185
186 #[test]
187 fn test_consume_helper() {
188 let segments = vec![make_segment("UNH"), make_segment("BGM")];
189 let mut cursor = SegmentCursor::new(segments.len());
190
191 let seg = consume(&segments, &mut cursor).unwrap();
192 assert_eq!(seg.id, "UNH");
193 assert_eq!(cursor.position(), 1);
194
195 let seg = consume(&segments, &mut cursor).unwrap();
196 assert_eq!(seg.id, "BGM");
197 assert!(cursor.is_exhausted());
198
199 assert!(consume(&segments, &mut cursor).is_none());
200 }
201
202 #[test]
203 fn test_expect_segment_helper() {
204 let segments = vec![make_segment("UNH"), make_segment("BGM")];
205 let mut cursor = SegmentCursor::new(segments.len());
206 let seg = expect_segment(&segments, &mut cursor, "UNH").unwrap();
207 assert_eq!(seg.id, "UNH");
208 assert_eq!(cursor.position(), 1);
209 }
210
211 #[test]
212 fn test_expect_segment_wrong_tag() {
213 let segments = vec![make_segment("UNH")];
214 let mut cursor = SegmentCursor::new(segments.len());
215 let result = expect_segment(&segments, &mut cursor, "BGM");
216 assert!(result.is_err());
217 }
218
219 #[test]
220 fn test_expect_segment_exhausted() {
221 let segments: Vec<OwnedSegment> = vec![];
222 let mut cursor = SegmentCursor::new(0);
223 let result = expect_segment(&segments, &mut cursor, "UNH");
224 assert!(result.is_err());
225 }
226}