1extern crate alloc;
4
5pub mod record;
6pub mod tuple;
7
8pub use record::RecordWriter;
9pub use tuple::TupleWriter;
10
11use alloc::borrow::{Cow, ToOwned};
12use alloc::string::String;
13use num_bigint::BigInt;
14
15use crate::document::InsertError;
16use crate::document::constructor::{DocumentConstructor, ScopeError};
17use crate::identifier::IdentifierError;
18use crate::parse::VariantPath;
19use crate::path::PathSegment;
20use crate::prelude_internal::*;
21use crate::text::Text;
22use crate::value::ValueKind;
23
24#[derive(Debug, thiserror::Error, Clone)]
26pub enum WriteError {
27 #[error("insert error: {0}")]
29 Insert(#[from] InsertError),
30
31 #[error("scope error: {0}")]
33 Scope(#[from] ScopeError),
34
35 #[error("invalid identifier: {0}")]
37 InvalidIdentifier(String),
38
39 #[error("invalid $variant extension type: expected text, got {actual}")]
41 InvalidVariantExtensionType { actual: ValueKind },
42
43 #[error("invalid $variant path: {source}")]
45 InvalidVariantPath { source: IdentifierError },
46
47 #[error("non-exhaustive enum variant for {type_name}")]
49 NonExhaustiveVariant { type_name: &'static str },
50}
51
52pub trait IntoEure<T = Self>: Sized {
90 fn write(value: T, c: &mut DocumentConstructor) -> Result<(), WriteError>;
92}
93
94impl IntoEure for bool {
99 fn write(value: bool, c: &mut DocumentConstructor) -> Result<(), WriteError> {
100 c.bind_primitive(PrimitiveValue::Bool(value))?;
101 Ok(())
102 }
103}
104
105macro_rules! impl_into_eure_int {
106 ($($ty:ty),*) => {
107 $(
108 impl IntoEure for $ty {
109 fn write(value: $ty, c: &mut DocumentConstructor) -> Result<(), WriteError> {
110 c.bind_primitive(PrimitiveValue::Integer(BigInt::from(value)))?;
111 Ok(())
112 }
113 }
114 )*
115 };
116}
117
118impl_into_eure_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
119
120impl IntoEure for f32 {
121 fn write(value: f32, c: &mut DocumentConstructor) -> Result<(), WriteError> {
122 c.bind_primitive(PrimitiveValue::F32(value))?;
123 Ok(())
124 }
125}
126
127impl IntoEure for f64 {
128 fn write(value: f64, c: &mut DocumentConstructor) -> Result<(), WriteError> {
129 c.bind_primitive(PrimitiveValue::F64(value))?;
130 Ok(())
131 }
132}
133
134impl IntoEure for BigInt {
135 fn write(value: BigInt, c: &mut DocumentConstructor) -> Result<(), WriteError> {
136 c.bind_primitive(PrimitiveValue::Integer(value))?;
137 Ok(())
138 }
139}
140
141impl IntoEure for String {
142 fn write(value: String, c: &mut DocumentConstructor) -> Result<(), WriteError> {
143 c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value)))?;
144 Ok(())
145 }
146}
147
148impl<'a> IntoEure for &'a str {
149 fn write(value: &'a str, c: &mut DocumentConstructor) -> Result<(), WriteError> {
150 c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value)))?;
151 Ok(())
152 }
153}
154
155impl<'a, T> IntoEure<Cow<'a, T>> for Cow<'a, T>
156where
157 T: ToOwned + ?Sized,
158 T::Owned: IntoEure,
159{
160 fn write(value: Cow<'a, T>, c: &mut DocumentConstructor) -> Result<(), WriteError> {
161 <T::Owned as IntoEure>::write(value.into_owned(), c)
162 }
163}
164
165impl IntoEure for Text {
166 fn write(value: Text, c: &mut DocumentConstructor) -> Result<(), WriteError> {
167 c.bind_primitive(PrimitiveValue::Text(value))?;
168 Ok(())
169 }
170}
171
172impl IntoEure for PrimitiveValue {
173 fn write(value: PrimitiveValue, c: &mut DocumentConstructor) -> Result<(), WriteError> {
174 c.bind_primitive(value)?;
175 Ok(())
176 }
177}
178
179impl IntoEure for Identifier {
180 fn write(value: Identifier, c: &mut DocumentConstructor) -> Result<(), WriteError> {
181 c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value.into_string())))?;
182 Ok(())
183 }
184}
185
186impl<M, T> IntoEure<Vec<T>> for Vec<M>
191where
192 M: IntoEure<T>,
193{
194 fn write(value: Vec<T>, c: &mut DocumentConstructor) -> Result<(), WriteError> {
195 c.bind_empty_array()?;
196 for item in value {
197 let scope = c.begin_scope();
198 c.navigate(PathSegment::ArrayIndex(None))?;
199 M::write(item, c)?;
200 c.end_scope(scope)?;
201 }
202 Ok(())
203 }
204}
205
206impl<M, T, const N: usize> IntoEure<[T; N]> for [M; N]
207where
208 M: IntoEure<T>,
209{
210 fn write(value: [T; N], c: &mut DocumentConstructor) -> Result<(), WriteError> {
211 c.bind_empty_array()?;
212 for item in value {
213 let scope = c.begin_scope();
214 c.navigate(PathSegment::ArrayIndex(None))?;
215 M::write(item, c)?;
216 c.end_scope(scope)?;
217 }
218 Ok(())
219 }
220}
221
222impl<M, K, V> IntoEure<Map<K, V>> for Map<K, M>
223where
224 M: IntoEure<V>,
225 K: Into<ObjectKey>,
226{
227 fn write(value: Map<K, V>, c: &mut DocumentConstructor) -> Result<(), WriteError> {
228 c.bind_empty_map()?;
229 for (key, v) in value {
230 let scope = c.begin_scope();
231 c.navigate(PathSegment::Value(key.into()))?;
232 M::write(v, c)?;
233 c.end_scope(scope)?;
234 }
235 Ok(())
236 }
237}
238
239impl<M, T> IntoEure<Option<T>> for Option<M>
240where
241 M: IntoEure<T>,
242{
243 fn write(value: Option<T>, c: &mut DocumentConstructor) -> Result<(), WriteError> {
244 match value {
245 Some(v) => M::write(v, c),
246 None => {
247 c.bind_primitive(PrimitiveValue::Null)?;
248 Ok(())
249 }
250 }
251 }
252}
253
254macro_rules! impl_into_document_tuple {
259 ($n:expr, $($idx:tt: $marker:ident : $ty:ident),+) => {
260 impl<$($marker, $ty),+> IntoEure<($($ty,)+)> for ($($marker,)+)
261 where
262 $($marker: IntoEure<$ty>),+
263 {
264 fn write(value: ($($ty,)+), c: &mut DocumentConstructor) -> Result<(), WriteError> {
265 c.bind_empty_tuple()?;
266 $(
267 let scope = c.begin_scope();
268 c.navigate(PathSegment::TupleIndex($idx))?;
269 $marker::write(value.$idx, c)?;
270 c.end_scope(scope)?;
271 )+
272 Ok(())
273 }
274 }
275 };
276}
277
278impl_into_document_tuple!(1, 0: MA: A);
279impl_into_document_tuple!(2, 0: MA: A, 1: MB: B);
280impl_into_document_tuple!(3, 0: MA: A, 1: MB: B, 2: MC: C);
281impl_into_document_tuple!(4, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D);
282impl_into_document_tuple!(5, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E);
283impl_into_document_tuple!(6, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F);
284impl_into_document_tuple!(7, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G);
285impl_into_document_tuple!(8, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H);
286impl_into_document_tuple!(9, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I);
287impl_into_document_tuple!(10, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J);
288impl_into_document_tuple!(11, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K);
289impl_into_document_tuple!(12, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K, 11: ML: L);
290impl_into_document_tuple!(13, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K, 11: ML: L, 12: MM: M);
291impl_into_document_tuple!(14, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K, 11: ML: L, 12: MM: M, 13: MN: N);
292impl_into_document_tuple!(15, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K, 11: ML: L, 12: MM: M, 13: MN: N, 14: MO: O);
293impl_into_document_tuple!(16, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F, 6: MG: G, 7: MH: H, 8: MI: I, 9: MJ: J, 10: MK: K, 11: ML: L, 12: MM: M, 13: MN: N, 14: MO: O, 15: MP: P);
294
295impl DocumentConstructor {
300 pub fn record<F, T>(&mut self, f: F) -> Result<T, WriteError>
312 where
313 F: FnOnce(&mut RecordWriter<'_>) -> Result<T, WriteError>,
314 {
315 self.bind_empty_map()?;
316 let mut writer = RecordWriter::new(self);
317 f(&mut writer)
318 }
319
320 pub fn tuple<F, T>(&mut self, f: F) -> Result<T, WriteError>
333 where
334 F: FnOnce(&mut TupleWriter<'_>) -> Result<T, WriteError>,
335 {
336 self.bind_empty_tuple()?;
337 let mut writer = TupleWriter::new(self);
338 f(&mut writer)
339 }
340
341 pub fn set_extension<T: IntoEure>(&mut self, name: &str, value: T) -> Result<(), WriteError> {
349 let ident: Identifier = name
350 .parse()
351 .map_err(|_| WriteError::InvalidIdentifier(name.into()))?;
352 let scope = self.begin_scope();
353 self.navigate(PathSegment::Extension(ident))?;
354 T::write(value, self)?;
355 self.end_scope(scope)?;
356 Ok(())
357 }
358
359 pub fn set_extension_optional<T: IntoEure>(
368 &mut self,
369 name: &str,
370 value: Option<T>,
371 ) -> Result<(), WriteError> {
372 if let Some(v) = value {
373 self.set_extension(name, v)?;
374 }
375 Ok(())
376 }
377
378 pub fn set_variant(&mut self, variant: &str) -> Result<(), WriteError> {
394 VariantPath::parse(variant)
395 .map_err(|err| WriteError::InvalidVariantPath { source: err })?;
396 let current_id = self.current_node_id();
397 if let Some(variant_node_id) = self
398 .document()
399 .node(current_id)
400 .get_extension(&Identifier::VARIANT)
401 {
402 let node = self.document().node(variant_node_id);
403 let existing = match node.as_primitive().and_then(|value| value.as_str()) {
404 Some(existing) => existing,
405 None => {
406 let actual = node.content.value_kind();
407 return Err(WriteError::InvalidVariantExtensionType { actual });
408 }
409 };
410 VariantPath::parse(existing)
411 .map_err(|err| WriteError::InvalidVariantPath { source: err })?;
412 let mut combined = String::with_capacity(existing.len() + 1 + variant.len());
413 combined.push_str(existing);
414 combined.push('.');
415 combined.push_str(variant);
416 self.document_mut().node_mut(variant_node_id).content =
417 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(combined)));
418 Ok(())
419 } else {
420 self.set_extension("variant", variant)
421 }
422 }
423
424 pub fn write<T: IntoEure>(&mut self, value: T) -> Result<(), WriteError> {
432 T::write(value, self)
433 }
434
435 pub fn write_via<M, T>(&mut self, value: T) -> Result<(), WriteError>
447 where
448 M: IntoEure<T>,
449 {
450 M::write(value, self)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_primitive_bool() {
460 let mut c = DocumentConstructor::new();
461 c.write(true).unwrap();
462 let doc = c.finish();
463 assert_eq!(
464 doc.root().content,
465 NodeValue::Primitive(PrimitiveValue::Bool(true))
466 );
467 }
468
469 #[test]
470 fn test_primitive_string() {
471 let mut c = DocumentConstructor::new();
472 c.write("hello").unwrap();
473 let doc = c.finish();
474 assert_eq!(
475 doc.root().content,
476 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
477 );
478 }
479
480 #[test]
481 fn test_vec() {
482 let mut c = DocumentConstructor::new();
483 c.write(vec![1i32, 2, 3]).unwrap();
484 let doc = c.finish();
485 let arr = doc.root().as_array().unwrap();
486 assert_eq!(arr.len(), 3);
487 }
488
489 #[test]
490 fn test_tuple() {
491 let mut c = DocumentConstructor::new();
492 c.write((1i32, "two", true)).unwrap();
493 let doc = c.finish();
494 let tuple = doc.root().as_tuple().unwrap();
495 assert_eq!(tuple.len(), 3);
496 }
497
498 #[test]
499 fn test_record() {
500 let mut c = DocumentConstructor::new();
501 c.record(|rec| {
502 rec.field("name", "Alice")?;
503 rec.field("age", 30i32)?;
504 Ok(())
505 })
506 .unwrap();
507 let doc = c.finish();
508 let map = doc.root().as_map().unwrap();
509 assert_eq!(map.len(), 2);
510 }
511
512 #[test]
513 fn test_set_extension() {
514 let mut c = DocumentConstructor::new();
515 c.record(|rec| {
516 rec.field("type", "string")?;
517 Ok(())
518 })
519 .unwrap();
520 c.set_extension("optional", true).unwrap();
521 let doc = c.finish();
522
523 let root = doc.root();
524 assert!(
525 root.extensions
526 .contains_key(&"optional".parse::<Identifier>().unwrap())
527 );
528 }
529
530 #[test]
531 fn test_set_variant() {
532 let mut c = DocumentConstructor::new();
533 c.set_variant("foo").unwrap();
534 c.record(|rec| {
535 rec.field("value", 42i32)?;
536 Ok(())
537 })
538 .unwrap();
539 let doc = c.finish();
540
541 let root = doc.root();
542 assert!(
543 root.extensions
544 .contains_key(&"variant".parse::<Identifier>().unwrap())
545 );
546 }
547
548 #[test]
549 fn test_set_variant_invalid_extension_type() {
550 let mut c = DocumentConstructor::new();
551 c.set_extension("variant", 1i32).unwrap();
552 let err = c.set_variant("foo").unwrap_err();
553 assert!(matches!(
554 err,
555 WriteError::InvalidVariantExtensionType {
556 actual: ValueKind::Integer
557 }
558 ));
559 }
560
561 #[test]
562 fn test_nested_record() {
563 let mut c = DocumentConstructor::new();
564 c.record(|rec| {
565 rec.field("name", "Alice")?;
566 rec.field_with("address", |c| {
567 c.record(|rec| {
568 rec.field("city", "Tokyo")?;
569 rec.field("zip", "100-0001")?;
570 Ok(())
571 })
572 })?;
573 Ok(())
574 })
575 .unwrap();
576 let doc = c.finish();
577 let map = doc.root().as_map().unwrap();
578 assert_eq!(map.len(), 2);
579 }
580
581 #[test]
582 fn test_write_via() {
583 struct DurationMarker;
585 struct DurationLike {
586 secs: u64,
587 nanos: u32,
588 }
589
590 impl IntoEure<DurationLike> for DurationMarker {
591 fn write(value: DurationLike, c: &mut DocumentConstructor) -> Result<(), WriteError> {
592 c.record(|rec| {
593 rec.field("secs", value.secs)?;
594 rec.field("nanos", value.nanos)?;
595 Ok(())
596 })
597 }
598 }
599
600 let mut c = DocumentConstructor::new();
601 c.write_via::<DurationMarker, _>(DurationLike {
602 secs: 60,
603 nanos: 123,
604 })
605 .unwrap();
606 let doc = c.finish();
607 let map = doc.root().as_map().unwrap();
608 assert_eq!(map.len(), 2);
609 }
610
611 #[test]
612 fn test_array_write() {
613 let mut c = DocumentConstructor::new();
614 c.write([1i32, 2, 3]).unwrap();
615 let doc = c.finish();
616 let arr = doc.root().as_array().unwrap();
617 assert_eq!(arr.len(), 3);
618 }
619
620 #[test]
621 fn test_array_empty_write() {
622 let mut c = DocumentConstructor::new();
623 let empty: [i32; 0] = [];
624 c.write(empty).unwrap();
625 let doc = c.finish();
626 let arr = doc.root().as_array().unwrap();
627 assert_eq!(arr.len(), 0);
628 }
629
630 #[test]
631 fn test_array_roundtrip() {
632 let original: [i32; 3] = [10, 20, 30];
633
634 let mut c = DocumentConstructor::new();
636 c.write(original).unwrap();
637 let doc = c.finish();
638
639 let root_id = doc.get_root_id();
641 let parsed: [i32; 3] = doc.parse(root_id).unwrap();
642
643 assert_eq!(parsed, original);
644 }
645
646 #[test]
651 fn test_cow_borrowed_str() {
652 let mut c = DocumentConstructor::new();
653 let value: Cow<'_, str> = Cow::Borrowed("hello");
654 c.write(value).unwrap();
655 let doc = c.finish();
656 assert_eq!(
657 doc.root().content,
658 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
659 );
660 }
661
662 #[test]
663 fn test_cow_owned_str() {
664 let mut c = DocumentConstructor::new();
665 let value: Cow<'static, str> = Cow::Owned("hello".to_string());
666 c.write(value).unwrap();
667 let doc = c.finish();
668 assert_eq!(
669 doc.root().content,
670 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
671 );
672 }
673}