eure_document/
write.rs

1//! IntoDocument trait for writing Rust types to Eure documents.
2
3extern crate alloc;
4
5pub mod record;
6pub mod tuple;
7
8pub use record::RecordWriter;
9pub use tuple::TupleWriter;
10
11use alloc::string::String;
12use num_bigint::BigInt;
13
14use crate::document::InsertError;
15use crate::document::constructor::{DocumentConstructor, ScopeError};
16use crate::path::PathSegment;
17use crate::prelude_internal::*;
18use crate::text::Text;
19
20/// Error type for write operations.
21#[derive(Debug, thiserror::Error, Clone)]
22pub enum WriteError {
23    /// Error during document insertion.
24    #[error("insert error: {0}")]
25    Insert(#[from] InsertError),
26
27    /// Error during scope management.
28    #[error("scope error: {0}")]
29    Scope(#[from] ScopeError),
30
31    /// Invalid identifier provided.
32    #[error("invalid identifier: {0}")]
33    InvalidIdentifier(String),
34}
35
36/// Trait for writing Rust types to Eure documents.
37///
38/// Types implementing this trait can be serialized into [`EureDocument`]
39/// via [`DocumentConstructor`].
40///
41/// # Examples
42///
43/// ```ignore
44/// impl IntoDocument for User {
45///     fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
46///         c.record(|rec| {
47///             rec.field("name", self.name)?;
48///             rec.field_optional("age", self.age)?;
49///             Ok(())
50///         })
51///     }
52/// }
53/// ```
54pub trait IntoDocument {
55    /// Write this value to the current node in the document constructor.
56    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError>;
57}
58
59// ============================================================================
60// Primitive implementations
61// ============================================================================
62
63impl IntoDocument for bool {
64    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
65        c.bind_primitive(PrimitiveValue::Bool(self))?;
66        Ok(())
67    }
68}
69
70impl IntoDocument for i32 {
71    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
72        c.bind_primitive(PrimitiveValue::Integer(BigInt::from(self)))?;
73        Ok(())
74    }
75}
76
77impl IntoDocument for i64 {
78    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
79        c.bind_primitive(PrimitiveValue::Integer(BigInt::from(self)))?;
80        Ok(())
81    }
82}
83
84impl IntoDocument for u32 {
85    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
86        c.bind_primitive(PrimitiveValue::Integer(BigInt::from(self)))?;
87        Ok(())
88    }
89}
90
91impl IntoDocument for u64 {
92    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
93        c.bind_primitive(PrimitiveValue::Integer(BigInt::from(self)))?;
94        Ok(())
95    }
96}
97
98impl IntoDocument for usize {
99    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
100        c.bind_primitive(PrimitiveValue::Integer(BigInt::from(self)))?;
101        Ok(())
102    }
103}
104
105impl IntoDocument for f32 {
106    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
107        c.bind_primitive(PrimitiveValue::F32(self))?;
108        Ok(())
109    }
110}
111
112impl IntoDocument for f64 {
113    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
114        c.bind_primitive(PrimitiveValue::F64(self))?;
115        Ok(())
116    }
117}
118
119impl IntoDocument for BigInt {
120    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
121        c.bind_primitive(PrimitiveValue::Integer(self))?;
122        Ok(())
123    }
124}
125
126impl IntoDocument for String {
127    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
128        c.bind_primitive(PrimitiveValue::Text(Text::plaintext(self)))?;
129        Ok(())
130    }
131}
132
133impl IntoDocument for &str {
134    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
135        c.bind_primitive(PrimitiveValue::Text(Text::plaintext(self)))?;
136        Ok(())
137    }
138}
139
140impl IntoDocument for Text {
141    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
142        c.bind_primitive(PrimitiveValue::Text(self))?;
143        Ok(())
144    }
145}
146
147impl IntoDocument for PrimitiveValue {
148    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
149        c.bind_primitive(self)?;
150        Ok(())
151    }
152}
153
154impl IntoDocument for Identifier {
155    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
156        c.bind_primitive(PrimitiveValue::Text(Text::plaintext(self.into_string())))?;
157        Ok(())
158    }
159}
160
161// ============================================================================
162// Collection implementations
163// ============================================================================
164
165impl<T: IntoDocument> IntoDocument for Vec<T> {
166    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
167        c.bind_empty_array()?;
168        for item in self {
169            let scope = c.begin_scope();
170            c.navigate(PathSegment::ArrayIndex(None))?;
171            item.write_to(c)?;
172            c.end_scope(scope)?;
173        }
174        Ok(())
175    }
176}
177
178impl<K, V> IntoDocument for Map<K, V>
179where
180    K: Into<ObjectKey>,
181    V: IntoDocument,
182{
183    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
184        c.bind_empty_map()?;
185        for (key, value) in self {
186            let scope = c.begin_scope();
187            c.navigate(PathSegment::Value(key.into()))?;
188            value.write_to(c)?;
189            c.end_scope(scope)?;
190        }
191        Ok(())
192    }
193}
194
195impl<T: IntoDocument> IntoDocument for Option<T> {
196    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
197        match self {
198            Some(value) => value.write_to(c),
199            None => {
200                c.bind_primitive(PrimitiveValue::Null)?;
201                Ok(())
202            }
203        }
204    }
205}
206
207// ============================================================================
208// Tuple implementations
209// ============================================================================
210
211macro_rules! impl_into_document_tuple {
212    ($n:expr, $($idx:tt: $var:ident),+) => {
213        impl<$($var: IntoDocument),+> IntoDocument for ($($var,)+) {
214            fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
215                c.bind_empty_tuple()?;
216                $(
217                    let scope = c.begin_scope();
218                    c.navigate(PathSegment::TupleIndex($idx))?;
219                    self.$idx.write_to(c)?;
220                    c.end_scope(scope)?;
221                )+
222                Ok(())
223            }
224        }
225    };
226}
227
228impl_into_document_tuple!(1, 0: A);
229impl_into_document_tuple!(2, 0: A, 1: B);
230impl_into_document_tuple!(3, 0: A, 1: B, 2: C);
231impl_into_document_tuple!(4, 0: A, 1: B, 2: C, 3: D);
232impl_into_document_tuple!(5, 0: A, 1: B, 2: C, 3: D, 4: E);
233impl_into_document_tuple!(6, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
234impl_into_document_tuple!(7, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
235impl_into_document_tuple!(8, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
236impl_into_document_tuple!(9, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
237impl_into_document_tuple!(10, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
238impl_into_document_tuple!(11, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K);
239impl_into_document_tuple!(12, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L);
240impl_into_document_tuple!(13, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L, 12: M);
241impl_into_document_tuple!(14, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L, 12: M, 13: N);
242impl_into_document_tuple!(15, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L, 12: M, 13: N, 14: O);
243impl_into_document_tuple!(16, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L, 12: M, 13: N, 14: O, 15: P);
244
245// ============================================================================
246// DocumentConstructor extensions
247// ============================================================================
248
249impl DocumentConstructor {
250    /// Write a record (map with string keys) using a closure.
251    ///
252    /// # Example
253    ///
254    /// ```ignore
255    /// c.record(|rec| {
256    ///     rec.field("name", "Alice")?;
257    ///     rec.field("age", 30)?;
258    ///     Ok(())
259    /// })?;
260    /// ```
261    pub fn record<F, T>(&mut self, f: F) -> Result<T, WriteError>
262    where
263        F: FnOnce(&mut RecordWriter<'_>) -> Result<T, WriteError>,
264    {
265        self.bind_empty_map()?;
266        let mut writer = RecordWriter::new(self);
267        f(&mut writer)
268    }
269
270    /// Write a tuple using a closure.
271    ///
272    /// # Example
273    ///
274    /// ```ignore
275    /// c.tuple(|t| {
276    ///     t.next("first")?;
277    ///     t.next(42)?;
278    ///     t.next(true)?;
279    ///     Ok(())
280    /// })?;
281    /// ```
282    pub fn tuple<F, T>(&mut self, f: F) -> Result<T, WriteError>
283    where
284        F: FnOnce(&mut TupleWriter<'_>) -> Result<T, WriteError>,
285    {
286        self.bind_empty_tuple()?;
287        let mut writer = TupleWriter::new(self);
288        f(&mut writer)
289    }
290
291    /// Set an extension value on the current node.
292    ///
293    /// # Example
294    ///
295    /// ```ignore
296    /// c.set_extension("optional", true)?;
297    /// ```
298    pub fn set_extension<T: IntoDocument>(
299        &mut self,
300        name: &str,
301        value: T,
302    ) -> Result<(), WriteError> {
303        let ident: Identifier = name
304            .parse()
305            .map_err(|_| WriteError::InvalidIdentifier(name.into()))?;
306        let scope = self.begin_scope();
307        self.navigate(PathSegment::Extension(ident))?;
308        value.write_to(self)?;
309        self.end_scope(scope)?;
310        Ok(())
311    }
312
313    /// Set an optional extension value on the current node.
314    /// Does nothing if the value is `None`.
315    ///
316    /// # Example
317    ///
318    /// ```ignore
319    /// c.set_extension_optional("default", self.default)?;
320    /// ```
321    pub fn set_extension_optional<T: IntoDocument>(
322        &mut self,
323        name: &str,
324        value: Option<T>,
325    ) -> Result<(), WriteError> {
326        if let Some(v) = value {
327            self.set_extension(name, v)?;
328        }
329        Ok(())
330    }
331
332    /// Set the `$variant` extension for union types.
333    ///
334    /// # Example
335    ///
336    /// ```ignore
337    /// match self {
338    ///     MyEnum::Foo(inner) => {
339    ///         c.set_variant("foo")?;
340    ///         inner.write_to(c)?;
341    ///     }
342    /// }
343    /// ```
344    pub fn set_variant(&mut self, variant: &str) -> Result<(), WriteError> {
345        self.set_extension("variant", variant)
346    }
347
348    /// Write a value implementing `IntoDocument` to the current node.
349    ///
350    /// # Example
351    ///
352    /// ```ignore
353    /// c.write(my_value)?;
354    /// ```
355    pub fn write<T: IntoDocument>(&mut self, value: T) -> Result<(), WriteError> {
356        value.write_to(self)
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363
364    #[test]
365    fn test_primitive_bool() {
366        let mut c = DocumentConstructor::new();
367        true.write_to(&mut c).unwrap();
368        let doc = c.finish();
369        assert_eq!(
370            doc.root().content,
371            NodeValue::Primitive(PrimitiveValue::Bool(true))
372        );
373    }
374
375    #[test]
376    fn test_primitive_string() {
377        let mut c = DocumentConstructor::new();
378        "hello".write_to(&mut c).unwrap();
379        let doc = c.finish();
380        assert_eq!(
381            doc.root().content,
382            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
383        );
384    }
385
386    #[test]
387    fn test_vec() {
388        let mut c = DocumentConstructor::new();
389        vec![1i32, 2, 3].write_to(&mut c).unwrap();
390        let doc = c.finish();
391        let arr = doc.root().as_array().unwrap();
392        assert_eq!(arr.len(), 3);
393    }
394
395    #[test]
396    fn test_tuple() {
397        let mut c = DocumentConstructor::new();
398        (1i32, "two", true).write_to(&mut c).unwrap();
399        let doc = c.finish();
400        let tuple = doc.root().as_tuple().unwrap();
401        assert_eq!(tuple.len(), 3);
402    }
403
404    #[test]
405    fn test_record() {
406        let mut c = DocumentConstructor::new();
407        c.record(|rec| {
408            rec.field("name", "Alice")?;
409            rec.field("age", 30i32)?;
410            Ok(())
411        })
412        .unwrap();
413        let doc = c.finish();
414        let map = doc.root().as_map().unwrap();
415        assert_eq!(map.len(), 2);
416    }
417
418    #[test]
419    fn test_set_extension() {
420        let mut c = DocumentConstructor::new();
421        c.record(|rec| {
422            rec.field("type", "string")?;
423            Ok(())
424        })
425        .unwrap();
426        c.set_extension("optional", true).unwrap();
427        let doc = c.finish();
428
429        let root = doc.root();
430        assert!(
431            root.extensions
432                .contains_key(&"optional".parse::<Identifier>().unwrap())
433        );
434    }
435
436    #[test]
437    fn test_set_variant() {
438        let mut c = DocumentConstructor::new();
439        c.set_variant("foo").unwrap();
440        c.record(|rec| {
441            rec.field("value", 42i32)?;
442            Ok(())
443        })
444        .unwrap();
445        let doc = c.finish();
446
447        let root = doc.root();
448        assert!(
449            root.extensions
450                .contains_key(&"variant".parse::<Identifier>().unwrap())
451        );
452    }
453
454    #[test]
455    fn test_nested_record() {
456        let mut c = DocumentConstructor::new();
457        c.record(|rec| {
458            rec.field("name", "Alice")?;
459            rec.field_with("address", |c| {
460                c.record(|rec| {
461                    rec.field("city", "Tokyo")?;
462                    rec.field("zip", "100-0001")?;
463                    Ok(())
464                })
465            })?;
466            Ok(())
467        })
468        .unwrap();
469        let doc = c.finish();
470        let map = doc.root().as_map().unwrap();
471        assert_eq!(map.len(), 2);
472    }
473}