Skip to main content

eure_document/
write.rs

1//! IntoEure 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 IntoEure 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 IntoEure {
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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure 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 IntoEure for PrimitiveValue {
148    fn write_to(self, c: &mut DocumentConstructor) -> Result<(), WriteError> {
149        c.bind_primitive(self)?;
150        Ok(())
151    }
152}
153
154impl IntoEure 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: IntoEure> IntoEure 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> IntoEure for Map<K, V>
179where
180    K: Into<ObjectKey>,
181    V: IntoEure,
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: IntoEure> IntoEure 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: IntoEure),+> IntoEure 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: IntoEure>(&mut self, name: &str, value: T) -> Result<(), WriteError> {
299        let ident: Identifier = name
300            .parse()
301            .map_err(|_| WriteError::InvalidIdentifier(name.into()))?;
302        let scope = self.begin_scope();
303        self.navigate(PathSegment::Extension(ident))?;
304        value.write_to(self)?;
305        self.end_scope(scope)?;
306        Ok(())
307    }
308
309    /// Set an optional extension value on the current node.
310    /// Does nothing if the value is `None`.
311    ///
312    /// # Example
313    ///
314    /// ```ignore
315    /// c.set_extension_optional("default", self.default)?;
316    /// ```
317    pub fn set_extension_optional<T: IntoEure>(
318        &mut self,
319        name: &str,
320        value: Option<T>,
321    ) -> Result<(), WriteError> {
322        if let Some(v) = value {
323            self.set_extension(name, v)?;
324        }
325        Ok(())
326    }
327
328    /// Set the `$variant` extension for union types.
329    ///
330    /// # Example
331    ///
332    /// ```ignore
333    /// match self {
334    ///     MyEnum::Foo(inner) => {
335    ///         c.set_variant("foo")?;
336    ///         inner.write_to(c)?;
337    ///     }
338    /// }
339    /// ```
340    pub fn set_variant(&mut self, variant: &str) -> Result<(), WriteError> {
341        self.set_extension("variant", variant)
342    }
343
344    /// Write a value implementing `IntoEure` to the current node.
345    ///
346    /// # Example
347    ///
348    /// ```ignore
349    /// c.write(my_value)?;
350    /// ```
351    pub fn write<T: IntoEure>(&mut self, value: T) -> Result<(), WriteError> {
352        value.write_to(self)
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359
360    #[test]
361    fn test_primitive_bool() {
362        let mut c = DocumentConstructor::new();
363        true.write_to(&mut c).unwrap();
364        let doc = c.finish();
365        assert_eq!(
366            doc.root().content,
367            NodeValue::Primitive(PrimitiveValue::Bool(true))
368        );
369    }
370
371    #[test]
372    fn test_primitive_string() {
373        let mut c = DocumentConstructor::new();
374        "hello".write_to(&mut c).unwrap();
375        let doc = c.finish();
376        assert_eq!(
377            doc.root().content,
378            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
379        );
380    }
381
382    #[test]
383    fn test_vec() {
384        let mut c = DocumentConstructor::new();
385        vec![1i32, 2, 3].write_to(&mut c).unwrap();
386        let doc = c.finish();
387        let arr = doc.root().as_array().unwrap();
388        assert_eq!(arr.len(), 3);
389    }
390
391    #[test]
392    fn test_tuple() {
393        let mut c = DocumentConstructor::new();
394        (1i32, "two", true).write_to(&mut c).unwrap();
395        let doc = c.finish();
396        let tuple = doc.root().as_tuple().unwrap();
397        assert_eq!(tuple.len(), 3);
398    }
399
400    #[test]
401    fn test_record() {
402        let mut c = DocumentConstructor::new();
403        c.record(|rec| {
404            rec.field("name", "Alice")?;
405            rec.field("age", 30i32)?;
406            Ok(())
407        })
408        .unwrap();
409        let doc = c.finish();
410        let map = doc.root().as_map().unwrap();
411        assert_eq!(map.len(), 2);
412    }
413
414    #[test]
415    fn test_set_extension() {
416        let mut c = DocumentConstructor::new();
417        c.record(|rec| {
418            rec.field("type", "string")?;
419            Ok(())
420        })
421        .unwrap();
422        c.set_extension("optional", true).unwrap();
423        let doc = c.finish();
424
425        let root = doc.root();
426        assert!(
427            root.extensions
428                .contains_key(&"optional".parse::<Identifier>().unwrap())
429        );
430    }
431
432    #[test]
433    fn test_set_variant() {
434        let mut c = DocumentConstructor::new();
435        c.set_variant("foo").unwrap();
436        c.record(|rec| {
437            rec.field("value", 42i32)?;
438            Ok(())
439        })
440        .unwrap();
441        let doc = c.finish();
442
443        let root = doc.root();
444        assert!(
445            root.extensions
446                .contains_key(&"variant".parse::<Identifier>().unwrap())
447        );
448    }
449
450    #[test]
451    fn test_nested_record() {
452        let mut c = DocumentConstructor::new();
453        c.record(|rec| {
454            rec.field("name", "Alice")?;
455            rec.field_with("address", |c| {
456                c.record(|rec| {
457                    rec.field("city", "Tokyo")?;
458                    rec.field("zip", "100-0001")?;
459                    Ok(())
460                })
461            })?;
462            Ok(())
463        })
464        .unwrap();
465        let doc = c.finish();
466        let map = doc.root().as_map().unwrap();
467        assert_eq!(map.len(), 2);
468    }
469}