eure_document/write/
tuple.rs

1//! TupleWriter for writing tuple types to Eure documents.
2
3extern crate alloc;
4
5use crate::document::constructor::DocumentConstructor;
6use crate::path::PathSegment;
7
8use super::{IntoDocument, WriteError};
9
10/// Helper for writing tuple types to Eure documents.
11///
12/// Used within the closure passed to [`DocumentConstructor::tuple`].
13/// Automatically tracks position, no manual index management needed.
14///
15/// # Example
16///
17/// ```ignore
18/// c.tuple(|t| {
19///     t.next("first")?;
20///     t.next(42)?;
21///     t.next(true)?;
22///     Ok(())
23/// })?;
24/// ```
25pub struct TupleWriter<'a> {
26    constructor: &'a mut DocumentConstructor,
27    position: u8,
28}
29
30impl<'a> TupleWriter<'a> {
31    /// Create a new TupleWriter.
32    pub(crate) fn new(constructor: &'a mut DocumentConstructor) -> Self {
33        Self {
34            constructor,
35            position: 0,
36        }
37    }
38
39    /// Write the next element, advancing position automatically.
40    ///
41    /// # Example
42    ///
43    /// ```ignore
44    /// t.next("value")?;
45    /// t.next(123)?;
46    /// ```
47    pub fn next<T: IntoDocument>(&mut self, value: T) -> Result<(), WriteError> {
48        let scope = self.constructor.begin_scope();
49        self.constructor
50            .navigate(PathSegment::TupleIndex(self.position))?;
51        value.write_to(self.constructor)?;
52        self.constructor.end_scope(scope)?;
53        self.position += 1;
54        Ok(())
55    }
56
57    /// Write the next element using a custom writer closure.
58    ///
59    /// Useful for nested structures that need custom handling.
60    ///
61    /// # Example
62    ///
63    /// ```ignore
64    /// t.next_with(|c| {
65    ///     c.record(|rec| {
66    ///         rec.field("inner", "value")?;
67    ///         Ok(())
68    ///     })
69    /// })?;
70    /// ```
71    pub fn next_with<F, R>(&mut self, f: F) -> Result<R, WriteError>
72    where
73        F: FnOnce(&mut DocumentConstructor) -> Result<R, WriteError>,
74    {
75        let scope = self.constructor.begin_scope();
76        self.constructor
77            .navigate(PathSegment::TupleIndex(self.position))?;
78        let result = f(self.constructor)?;
79        self.constructor.end_scope(scope)?;
80        self.position += 1;
81        Ok(result)
82    }
83
84    /// Get the current position (number of elements written).
85    pub fn position(&self) -> u8 {
86        self.position
87    }
88
89    /// Get a mutable reference to the underlying DocumentConstructor.
90    ///
91    /// Useful for advanced use cases that need direct access.
92    pub fn constructor(&mut self) -> &mut DocumentConstructor {
93        self.constructor
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use alloc::string::ToString;
100
101    use super::*;
102    use crate::document::node::NodeValue;
103    use crate::text::Text;
104    use crate::value::{ObjectKey, PrimitiveValue};
105
106    #[test]
107    fn test_next_sequential() {
108        let mut c = DocumentConstructor::new();
109        c.tuple(|t| {
110            t.next(1i32)?;
111            t.next("two")?;
112            t.next(true)?;
113            Ok(())
114        })
115        .unwrap();
116        let doc = c.finish();
117        let tuple = doc.root().as_tuple().unwrap();
118        assert_eq!(tuple.len(), 3);
119    }
120
121    #[test]
122    fn test_next_with_nested() {
123        let mut c = DocumentConstructor::new();
124        c.tuple(|t| {
125            t.next("first")?;
126            t.next_with(|c| {
127                c.record(|rec| {
128                    rec.field("inner", "value")?;
129                    Ok(())
130                })
131            })?;
132            Ok(())
133        })
134        .unwrap();
135        let doc = c.finish();
136        let tuple = doc.root().as_tuple().unwrap();
137        assert_eq!(tuple.len(), 2);
138
139        // Check nested record
140        let nested_id = tuple.get(1).unwrap();
141        let nested = doc.node(nested_id).as_map().unwrap();
142        assert!(
143            nested
144                .get(&ObjectKey::String("inner".to_string()))
145                .is_some()
146        );
147    }
148
149    #[test]
150    fn test_position_tracking() {
151        let mut c = DocumentConstructor::new();
152        c.tuple(|t| {
153            assert_eq!(t.position(), 0);
154            t.next(1i32)?;
155            assert_eq!(t.position(), 1);
156            t.next(2i32)?;
157            assert_eq!(t.position(), 2);
158            Ok(())
159        })
160        .unwrap();
161    }
162
163    #[test]
164    fn test_empty_tuple() {
165        let mut c = DocumentConstructor::new();
166        c.tuple(|_t| Ok(())).unwrap();
167        let doc = c.finish();
168        let tuple = doc.root().as_tuple().unwrap();
169        assert!(tuple.is_empty());
170    }
171
172    #[test]
173    fn test_values_written_correctly() {
174        let mut c = DocumentConstructor::new();
175        c.tuple(|t| {
176            t.next(42i32)?;
177            t.next("hello")?;
178            Ok(())
179        })
180        .unwrap();
181        let doc = c.finish();
182        let tuple = doc.root().as_tuple().unwrap();
183
184        // Check first element
185        let first_id = tuple.get(0).unwrap();
186        assert_eq!(
187            doc.node(first_id).content,
188            NodeValue::Primitive(PrimitiveValue::Integer(42.into()))
189        );
190
191        // Check second element
192        let second_id = tuple.get(1).unwrap();
193        assert_eq!(
194            doc.node(second_id).content,
195            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
196        );
197    }
198}