eure_document/parse/
tuple.rs

1//! TupleParser for parsing tuple types from Eure documents.
2
3extern crate alloc;
4
5use alloc::format;
6
7use crate::document::node::NodeTuple;
8use crate::prelude_internal::*;
9
10use super::{ParseContext, ParseDocument, ParseError, ParseErrorKind};
11
12/// Helper for parsing tuple types from Eure documents.
13///
14/// Provides both sequential access via `next()` and random access via `get()`.
15/// Use `finish()` to verify all elements were consumed.
16///
17/// # Example
18///
19/// ```ignore
20/// let mut tuple = ctx.parse_tuple()?;
21/// let first: String = tuple.next()?;
22/// let second: i32 = tuple.next()?;
23/// tuple.finish()?; // Ensures no extra elements
24/// ```
25#[derive(Debug)]
26#[must_use]
27pub struct TupleParser<'doc> {
28    doc: &'doc EureDocument,
29    node_id: NodeId,
30    tuple: &'doc NodeTuple,
31    position: usize,
32}
33
34impl<'doc> TupleParser<'doc> {
35    /// Create a new TupleParser for the given context.
36    pub(crate) fn new(ctx: &ParseContext<'doc>) -> Result<Self, ParseError> {
37        Self::from_doc_and_node(ctx.doc(), ctx.node_id())
38    }
39
40    /// Create a new TupleParser from document and node ID directly.
41    pub(crate) fn from_doc_and_node(
42        doc: &'doc EureDocument,
43        node_id: NodeId,
44    ) -> Result<Self, ParseError> {
45        let node = doc.node(node_id);
46        match &node.content {
47            NodeValue::Tuple(tuple) => Ok(Self {
48                doc,
49                node_id,
50                tuple,
51                position: 0,
52            }),
53            NodeValue::Hole(_) => Err(ParseError {
54                node_id,
55                kind: ParseErrorKind::UnexpectedHole,
56            }),
57            value => Err(ParseError {
58                node_id,
59                kind: value
60                    .value_kind()
61                    .map(|actual| ParseErrorKind::TypeMismatch {
62                        expected: crate::value::ValueKind::Tuple,
63                        actual,
64                    })
65                    .unwrap_or(ParseErrorKind::UnexpectedHole),
66            }),
67        }
68    }
69
70    /// Get the node ID being parsed.
71    pub fn node_id(&self) -> NodeId {
72        self.node_id
73    }
74
75    /// Get the next element, advancing the position.
76    ///
77    /// Returns `ParseErrorKind::MissingField` if no more elements.
78    #[allow(clippy::should_implement_trait)]
79    pub fn next<T>(&mut self) -> Result<T, T::Error>
80    where
81        T: ParseDocument<'doc>,
82        T::Error: From<ParseError>,
83    {
84        let index = self.position;
85        let element_node_id = self.tuple.get(index).ok_or_else(|| ParseError {
86            node_id: self.node_id,
87            kind: ParseErrorKind::MissingField(format!("#{}", index)),
88        })?;
89        self.position += 1;
90        let ctx = ParseContext::new(self.doc, element_node_id);
91        T::parse(&ctx)
92    }
93
94    /// Get the element at a specific index without advancing position.
95    ///
96    /// Returns `ParseErrorKind::MissingField` if the index is out of bounds.
97    pub fn get<T>(&self, index: usize) -> Result<T, T::Error>
98    where
99        T: ParseDocument<'doc>,
100        T::Error: From<ParseError>,
101    {
102        let element_node_id = self.tuple.get(index).ok_or_else(|| ParseError {
103            node_id: self.node_id,
104            kind: ParseErrorKind::MissingField(format!("#{}", index)),
105        })?;
106        let ctx = ParseContext::new(self.doc, element_node_id);
107        T::parse(&ctx)
108    }
109
110    /// Get the number of remaining elements.
111    pub fn remaining(&self) -> usize {
112        self.tuple.len().saturating_sub(self.position)
113    }
114
115    /// Verify all elements were consumed.
116    ///
117    /// Returns `ParseErrorKind::UnexpectedTupleLength` if elements remain.
118    pub fn finish(self) -> Result<(), ParseError> {
119        if self.position != self.tuple.len() {
120            return Err(ParseError {
121                node_id: self.node_id,
122                kind: ParseErrorKind::UnexpectedTupleLength {
123                    expected: self.position,
124                    actual: self.tuple.len(),
125                },
126            });
127        }
128        Ok(())
129    }
130
131    /// Verify the tuple has the expected length.
132    ///
133    /// Returns `ParseErrorKind::UnexpectedTupleLength` if length doesn't match.
134    pub fn expect_len(&self, expected: usize) -> Result<(), ParseError> {
135        if self.tuple.len() != expected {
136            return Err(ParseError {
137                node_id: self.node_id,
138                kind: ParseErrorKind::UnexpectedTupleLength {
139                    expected,
140                    actual: self.tuple.len(),
141                },
142            });
143        }
144        Ok(())
145    }
146
147    /// Get the total number of elements in the tuple.
148    pub fn len(&self) -> usize {
149        self.tuple.len()
150    }
151
152    /// Check if the tuple is empty.
153    pub fn is_empty(&self) -> bool {
154        self.tuple.is_empty()
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::document::constructor::DocumentConstructor;
162    use crate::path::PathSegment;
163    use crate::value::PrimitiveValue;
164
165    fn create_tuple_doc(elements: Vec<PrimitiveValue>) -> EureDocument {
166        let mut c = DocumentConstructor::new();
167        c.bind_empty_tuple().unwrap();
168        for (i, elem) in elements.into_iter().enumerate() {
169            let scope = c.begin_scope();
170            c.navigate(PathSegment::TupleIndex(i as u8)).unwrap();
171            c.bind_primitive(elem).unwrap();
172            c.end_scope(scope).unwrap();
173        }
174        c.finish()
175    }
176
177    #[test]
178    fn test_next_sequential() {
179        let doc = create_tuple_doc(vec![
180            PrimitiveValue::Integer(1.into()),
181            PrimitiveValue::Integer(2.into()),
182            PrimitiveValue::Integer(3.into()),
183        ]);
184
185        let mut tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
186        assert_eq!(tuple.next::<i32>().unwrap(), 1);
187        assert_eq!(tuple.next::<i32>().unwrap(), 2);
188        assert_eq!(tuple.next::<i32>().unwrap(), 3);
189        tuple.finish().unwrap();
190    }
191
192    #[test]
193    fn test_next_past_end() {
194        let doc = create_tuple_doc(vec![PrimitiveValue::Integer(1.into())]);
195
196        let mut tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
197        tuple.next::<i32>().unwrap();
198        let result = tuple.next::<i32>();
199        assert!(matches!(
200            result.unwrap_err().kind,
201            ParseErrorKind::MissingField(_)
202        ));
203    }
204
205    #[test]
206    fn test_get_random_access() {
207        let doc = create_tuple_doc(vec![
208            PrimitiveValue::Integer(10.into()),
209            PrimitiveValue::Integer(20.into()),
210            PrimitiveValue::Integer(30.into()),
211        ]);
212
213        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
214        assert_eq!(tuple.get::<i32>(2).unwrap(), 30);
215        assert_eq!(tuple.get::<i32>(0).unwrap(), 10);
216        assert_eq!(tuple.get::<i32>(1).unwrap(), 20);
217    }
218
219    #[test]
220    fn test_get_out_of_bounds() {
221        let doc = create_tuple_doc(vec![PrimitiveValue::Integer(1.into())]);
222
223        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
224        let result = tuple.get::<i32>(5);
225        assert!(matches!(
226            result.unwrap_err().kind,
227            ParseErrorKind::MissingField(_)
228        ));
229    }
230
231    #[test]
232    fn test_remaining() {
233        let doc = create_tuple_doc(vec![
234            PrimitiveValue::Integer(1.into()),
235            PrimitiveValue::Integer(2.into()),
236            PrimitiveValue::Integer(3.into()),
237        ]);
238
239        let mut tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
240        assert_eq!(tuple.remaining(), 3);
241        tuple.next::<i32>().unwrap();
242        assert_eq!(tuple.remaining(), 2);
243        tuple.next::<i32>().unwrap();
244        assert_eq!(tuple.remaining(), 1);
245        tuple.next::<i32>().unwrap();
246        assert_eq!(tuple.remaining(), 0);
247    }
248
249    #[test]
250    fn test_finish_with_remaining_elements() {
251        let doc = create_tuple_doc(vec![
252            PrimitiveValue::Integer(1.into()),
253            PrimitiveValue::Integer(2.into()),
254        ]);
255
256        let mut tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
257        tuple.next::<i32>().unwrap();
258        // Only consumed 1 of 2 elements
259        let result = tuple.finish();
260        assert!(matches!(
261            result.unwrap_err().kind,
262            ParseErrorKind::UnexpectedTupleLength {
263                expected: 1,
264                actual: 2
265            }
266        ));
267    }
268
269    #[test]
270    fn test_expect_len_correct() {
271        let doc = create_tuple_doc(vec![
272            PrimitiveValue::Integer(1.into()),
273            PrimitiveValue::Integer(2.into()),
274        ]);
275
276        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
277        tuple.expect_len(2).unwrap();
278    }
279
280    #[test]
281    fn test_expect_len_incorrect() {
282        let doc = create_tuple_doc(vec![PrimitiveValue::Integer(1.into())]);
283
284        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
285        let result = tuple.expect_len(3);
286        assert!(matches!(
287            result.unwrap_err().kind,
288            ParseErrorKind::UnexpectedTupleLength {
289                expected: 3,
290                actual: 1
291            }
292        ));
293    }
294
295    #[test]
296    fn test_empty_tuple() {
297        let doc = create_tuple_doc(vec![]);
298
299        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
300        assert!(tuple.is_empty());
301        assert_eq!(tuple.len(), 0);
302        assert_eq!(tuple.remaining(), 0);
303        tuple.finish().unwrap();
304    }
305
306    #[test]
307    fn test_len_and_is_empty() {
308        let doc = create_tuple_doc(vec![
309            PrimitiveValue::Integer(1.into()),
310            PrimitiveValue::Integer(2.into()),
311        ]);
312
313        let tuple = doc.parse_tuple(doc.get_root_id()).unwrap();
314        assert!(!tuple.is_empty());
315        assert_eq!(tuple.len(), 2);
316    }
317
318    #[test]
319    fn test_parse_non_tuple_fails() {
320        let mut c = DocumentConstructor::new();
321        c.bind_empty_array().unwrap();
322        let doc = c.finish();
323
324        let result = doc.parse_tuple(doc.get_root_id());
325        assert!(matches!(
326            result.unwrap_err().kind,
327            ParseErrorKind::TypeMismatch { .. }
328        ));
329    }
330}