Skip to main content

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