extern crate alloc;
pub mod record;
pub mod tuple;
pub use record::RecordWriter;
pub use tuple::TupleWriter;
use crate::document::constructor::DocumentConstructor;
use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use num_bigint::BigInt;
use indexmap::IndexMap;
use crate::document::InsertError;
use crate::document::constructor::ScopeError;
use crate::identifier::IdentifierError;
use crate::parse::VariantPath;
use crate::path::{ArrayIndexKind, PathSegment};
use crate::prelude_internal::*;
use crate::text::Text;
use crate::value::ValueKind;
use core::any::type_name;
#[derive(Debug, thiserror::Error, Clone)]
pub enum WriteError {
#[error("insert error: {0}")]
Insert(#[from] InsertError),
#[error("scope error: {0}")]
Scope(#[from] ScopeError),
#[error("invalid identifier: {0}")]
InvalidIdentifier(String),
#[error("invalid $variant extension type: expected text, got {actual}")]
InvalidVariantExtensionType { actual: ValueKind },
#[error("invalid $variant path: {source}")]
InvalidVariantPath { source: IdentifierError },
#[error("non-exhaustive enum variant for {type_name}")]
NonExhaustiveVariant { type_name: &'static str },
#[error("flatten target is not record-like: {type_name}")]
FlattenTargetNotRecordLike { type_name: &'static str },
}
pub trait IntoEure<T = Self>: Sized {
type Error: From<WriteError>;
fn write(value: T, c: &mut DocumentConstructor) -> Result<(), Self::Error>;
fn write_flatten(value: T, rec: &mut RecordWriter<'_>) -> Result<(), Self::Error> {
let _ = value;
let _ = rec;
Err(WriteError::FlattenTargetNotRecordLike {
type_name: type_name::<T>(),
}
.into())
}
}
impl IntoEure for EureDocument {
type Error = WriteError;
fn write(value: Self, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.write_subtree(&value, value.get_root_id())
}
}
fn write_subtree_node(
src: &EureDocument,
node_id: NodeId,
c: &mut DocumentConstructor,
) -> Result<(), WriteError> {
let node = src.node(node_id);
match &node.content {
NodeValue::Hole(label) => {
c.bind_hole(label.clone())?;
}
NodeValue::Primitive(prim) => {
c.bind_primitive(prim.clone())?;
}
NodeValue::Array(array) => {
c.bind_empty_array()?;
for &child_id in array.iter() {
let scope = c.begin_scope();
c.navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))?;
write_subtree_node(src, child_id, c)?;
c.end_scope(scope)?;
}
}
NodeValue::Tuple(tuple) => {
c.bind_empty_tuple()?;
for (index, &child_id) in tuple.iter().enumerate() {
let scope = c.begin_scope();
c.navigate(PathSegment::TupleIndex(index as u8))?;
write_subtree_node(src, child_id, c)?;
c.end_scope(scope)?;
}
}
NodeValue::Map(map) => {
c.bind_empty_map()?;
for (key, &child_id) in map.iter() {
let scope = c.begin_scope();
c.navigate(PathSegment::Value(key.clone()))?;
write_subtree_node(src, child_id, c)?;
c.end_scope(scope)?;
}
}
NodeValue::PartialMap(map) => {
c.bind_empty_partial_map()?;
for (key, &child_id) in map.iter() {
let scope = c.begin_scope();
c.navigate_partial_map_entry(key.clone())?;
write_subtree_node(src, child_id, c)?;
c.end_scope(scope)?;
}
}
}
for (ident, &ext_node_id) in node.extensions.iter() {
let scope = c.begin_scope();
c.navigate(PathSegment::Extension(ident.clone()))?;
write_subtree_node(src, ext_node_id, c)?;
c.end_scope(scope)?;
}
Ok(())
}
impl IntoEure for bool {
type Error = WriteError;
fn write(value: bool, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Bool(value))
.map_err(WriteError::from)?;
Ok(())
}
}
macro_rules! impl_into_eure_int {
($($ty:ty),*) => {
$(
impl IntoEure for $ty {
type Error = WriteError;
fn write(value: $ty, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Integer(BigInt::from(value)))
.map_err(WriteError::from)?;
Ok(())
}
}
)*
};
}
impl_into_eure_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
impl IntoEure for f32 {
type Error = WriteError;
fn write(value: f32, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::F32(value))
.map_err(WriteError::from)?;
Ok(())
}
}
impl IntoEure for f64 {
type Error = WriteError;
fn write(value: f64, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::F64(value))
.map_err(WriteError::from)?;
Ok(())
}
}
impl IntoEure for BigInt {
type Error = WriteError;
fn write(value: BigInt, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Integer(value))
.map_err(WriteError::from)?;
Ok(())
}
}
impl IntoEure for String {
type Error = WriteError;
fn write(value: String, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value)))
.map_err(WriteError::from)?;
Ok(())
}
}
impl<'a> IntoEure for &'a str {
type Error = WriteError;
fn write(value: &'a str, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value)))
.map_err(WriteError::from)?;
Ok(())
}
}
impl<'a, T> IntoEure<Cow<'a, T>> for Cow<'a, T>
where
T: ToOwned + ?Sized,
T::Owned: IntoEure,
{
type Error = <T::Owned as IntoEure>::Error;
fn write(value: Cow<'a, T>, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
<T::Owned as IntoEure>::write(value.into_owned(), c)
}
}
impl IntoEure for Text {
type Error = WriteError;
fn write(value: Text, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Text(value))
.map_err(WriteError::from)?;
Ok(())
}
}
impl IntoEure for PrimitiveValue {
type Error = WriteError;
fn write(value: PrimitiveValue, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(value).map_err(WriteError::from)?;
Ok(())
}
}
impl IntoEure for Identifier {
type Error = WriteError;
fn write(value: Identifier, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_primitive(PrimitiveValue::Text(Text::plaintext(value.into_string())))
.map_err(WriteError::from)?;
Ok(())
}
}
impl<M, T> IntoEure<Vec<T>> for Vec<M>
where
M: IntoEure<T>,
{
type Error = M::Error;
fn write(value: Vec<T>, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_empty_array().map_err(WriteError::from)?;
for item in value {
let scope = c.begin_scope();
c.navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
.map_err(WriteError::from)?;
M::write(item, c)?;
c.end_scope(scope).map_err(WriteError::from)?;
}
Ok(())
}
}
impl<M, T, const N: usize> IntoEure<[T; N]> for [M; N]
where
M: IntoEure<T>,
{
type Error = M::Error;
fn write(value: [T; N], c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_empty_array().map_err(WriteError::from)?;
for item in value {
let scope = c.begin_scope();
c.navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
.map_err(WriteError::from)?;
M::write(item, c)?;
c.end_scope(scope).map_err(WriteError::from)?;
}
Ok(())
}
}
impl<M, K, V> IntoEure<Map<K, V>> for Map<K, M>
where
M: IntoEure<V>,
K: Into<ObjectKey>,
{
type Error = M::Error;
fn write(value: Map<K, V>, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_empty_map().map_err(WriteError::from)?;
for (key, v) in value {
let scope = c.begin_scope();
c.navigate(PathSegment::Value(key.into()))
.map_err(WriteError::from)?;
M::write(v, c)?;
c.end_scope(scope).map_err(WriteError::from)?;
}
Ok(())
}
fn write_flatten(value: Map<K, V>, rec: &mut RecordWriter<'_>) -> Result<(), Self::Error> {
for (key, v) in value {
let key = match key.into() {
ObjectKey::String(name) => name,
_ => {
return Err(WriteError::FlattenTargetNotRecordLike {
type_name: type_name::<Map<K, V>>(),
}
.into());
}
};
rec.field_via::<M, _>(&key, v)?;
}
Ok(())
}
}
impl<M, K, V> IntoEure<IndexMap<K, V>> for IndexMap<K, M>
where
M: IntoEure<V>,
K: Into<ObjectKey> + Eq + std::hash::Hash,
{
type Error = M::Error;
fn write(value: IndexMap<K, V>, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_empty_map().map_err(WriteError::from)?;
for (key, v) in value {
let scope = c.begin_scope();
c.navigate(PathSegment::Value(key.into()))
.map_err(WriteError::from)?;
M::write(v, c)?;
c.end_scope(scope).map_err(WriteError::from)?;
}
Ok(())
}
fn write_flatten(value: IndexMap<K, V>, rec: &mut RecordWriter<'_>) -> Result<(), Self::Error> {
for (key, v) in value {
let key = match key.into() {
ObjectKey::String(name) => name,
_ => {
return Err(WriteError::FlattenTargetNotRecordLike {
type_name: type_name::<IndexMap<K, V>>(),
}
.into());
}
};
rec.field_via::<M, _>(&key, v)?;
}
Ok(())
}
}
impl<M, T> IntoEure<Option<T>> for Option<M>
where
M: IntoEure<T>,
{
type Error = M::Error;
fn write(value: Option<T>, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
match value {
Some(v) => M::write(v, c),
None => {
c.bind_primitive(PrimitiveValue::Null)
.map_err(WriteError::from)?;
Ok(())
}
}
}
}
macro_rules! impl_into_document_tuple {
($n:expr, $($idx:tt: $marker:ident : $ty:ident),+) => {
impl<$($marker, $ty),+, __E> IntoEure<($($ty,)+)> for ($($marker,)+)
where
$($marker: IntoEure<$ty, Error = __E>,)+
__E: From<WriteError>,
{
type Error = __E;
fn write(value: ($($ty,)+), c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.bind_empty_tuple().map_err(WriteError::from)?;
$(
let scope = c.begin_scope();
c.navigate(PathSegment::TupleIndex($idx)).map_err(WriteError::from)?;
$marker::write(value.$idx, c)?;
c.end_scope(scope).map_err(WriteError::from)?;
)+
Ok(())
}
}
};
}
impl_into_document_tuple!(1, 0: MA: A);
impl_into_document_tuple!(2, 0: MA: A, 1: MB: B);
impl_into_document_tuple!(3, 0: MA: A, 1: MB: B, 2: MC: C);
impl_into_document_tuple!(4, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D);
impl_into_document_tuple!(5, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E);
impl_into_document_tuple!(6, 0: MA: A, 1: MB: B, 2: MC: C, 3: MD: D, 4: ME: E, 5: MF: F);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl_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);
impl DocumentConstructor {
pub fn record<F, T, E>(&mut self, f: F) -> Result<T, E>
where
F: FnOnce(&mut RecordWriter<'_>) -> Result<T, E>,
E: From<WriteError>,
{
self.bind_empty_map().map_err(WriteError::from)?;
let mut writer = RecordWriter::new(self);
f(&mut writer)
}
pub fn tuple<F, T, E>(&mut self, f: F) -> Result<T, E>
where
F: FnOnce(&mut TupleWriter<'_>) -> Result<T, E>,
E: From<WriteError>,
{
self.bind_empty_tuple().map_err(WriteError::from)?;
let mut writer = TupleWriter::new(self);
f(&mut writer)
}
pub fn set_extension<T: IntoEure>(&mut self, name: &str, value: T) -> Result<(), T::Error> {
let ident: Identifier = name
.parse()
.map_err(|_| WriteError::InvalidIdentifier(name.into()))?;
let scope = self.begin_scope();
self.navigate(PathSegment::Extension(ident))
.map_err(WriteError::from)?;
T::write(value, self)?;
self.end_scope(scope).map_err(WriteError::from)?;
Ok(())
}
pub fn set_extension_optional<T: IntoEure>(
&mut self,
name: &str,
value: Option<T>,
) -> Result<(), T::Error> {
if let Some(v) = value {
self.set_extension(name, v)?;
}
Ok(())
}
pub fn set_variant(&mut self, variant: &str) -> Result<(), WriteError> {
VariantPath::parse(variant)
.map_err(|err| WriteError::InvalidVariantPath { source: err })?;
let current_id = self.current_node_id();
if let Some(variant_node_id) = self
.document()
.node(current_id)
.get_extension(&Identifier::VARIANT)
{
let node = self.document().node(variant_node_id);
let existing = match node.as_primitive().and_then(|value| value.as_str()) {
Some(existing) => existing,
None => {
let actual = node.content.value_kind();
return Err(WriteError::InvalidVariantExtensionType { actual });
}
};
VariantPath::parse(existing)
.map_err(|err| WriteError::InvalidVariantPath { source: err })?;
let mut combined = String::with_capacity(existing.len() + 1 + variant.len());
combined.push_str(existing);
combined.push('.');
combined.push_str(variant);
self.document_mut().node_mut(variant_node_id).content =
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(combined)));
Ok(())
} else {
self.set_extension("variant", variant)
}
}
pub fn write_subtree(&mut self, src: &EureDocument, node_id: NodeId) -> Result<(), WriteError> {
write_subtree_node(src, node_id, self)
}
pub fn write<T: IntoEure>(&mut self, value: T) -> Result<(), T::Error> {
T::write(value, self)
}
pub fn write_via<M, T>(&mut self, value: T) -> Result<(), M::Error>
where
M: IntoEure<T>,
{
M::write(value, self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_primitive_bool() {
let mut c = DocumentConstructor::new();
c.write(true).unwrap();
let doc = c.finish();
assert_eq!(
doc.root().content,
NodeValue::Primitive(PrimitiveValue::Bool(true))
);
}
#[test]
fn test_primitive_string() {
let mut c = DocumentConstructor::new();
c.write("hello").unwrap();
let doc = c.finish();
assert_eq!(
doc.root().content,
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
);
}
#[test]
fn test_vec() {
let mut c = DocumentConstructor::new();
c.write(vec![1i32, 2, 3]).unwrap();
let doc = c.finish();
let arr = doc.root().as_array().unwrap();
assert_eq!(arr.len(), 3);
}
#[test]
fn test_tuple() {
let mut c = DocumentConstructor::new();
c.write((1i32, "two", true)).unwrap();
let doc = c.finish();
let tuple = doc.root().as_tuple().unwrap();
assert_eq!(tuple.len(), 3);
}
#[test]
fn test_tuple_roundtrip() {
let original = (42i32, "hello".to_string(), true);
let mut c = DocumentConstructor::new();
c.write(original.clone()).unwrap();
let doc = c.finish();
assert_eq!(eure!({=(42i32, "hello", true)}), doc);
let root_id = doc.get_root_id();
let parsed: (i32, String, bool) = doc.parse(root_id).unwrap();
assert_eq!(parsed, original);
}
#[test]
fn test_record() {
let mut c = DocumentConstructor::new();
c.record(|rec| {
rec.field("name", "Alice")?;
rec.field("age", 30i32)?;
Ok::<(), WriteError>(())
})
.unwrap();
let doc = c.finish();
let map = doc.root().as_map().unwrap();
assert_eq!(map.len(), 2);
}
#[test]
fn test_set_extension() {
let mut c = DocumentConstructor::new();
c.record(|rec| {
rec.field("type", "string")?;
Ok::<(), WriteError>(())
})
.unwrap();
c.set_extension("optional", true).unwrap();
let doc = c.finish();
let root = doc.root();
assert!(
root.extensions
.contains_key(&"optional".parse::<Identifier>().unwrap())
);
}
#[test]
fn test_set_variant() {
let mut c = DocumentConstructor::new();
c.set_variant("foo").unwrap();
c.record(|rec| {
rec.field("value", 42i32)?;
Ok::<(), WriteError>(())
})
.unwrap();
let doc = c.finish();
let root = doc.root();
assert!(
root.extensions
.contains_key(&"variant".parse::<Identifier>().unwrap())
);
}
#[test]
fn test_set_variant_invalid_extension_type() {
let mut c = DocumentConstructor::new();
c.set_extension("variant", 1i32).unwrap();
let err = c.set_variant("foo").unwrap_err();
assert!(matches!(
err,
WriteError::InvalidVariantExtensionType {
actual: ValueKind::Integer
}
));
}
#[test]
fn test_nested_record() {
let mut c = DocumentConstructor::new();
c.record(|rec| {
rec.field("name", "Alice")?;
rec.field_with("address", |c| {
c.record(|rec| {
rec.field("city", "Tokyo")?;
rec.field("zip", "100-0001")?;
Ok::<(), WriteError>(())
})
})?;
Ok::<(), WriteError>(())
})
.unwrap();
let doc = c.finish();
let map = doc.root().as_map().unwrap();
assert_eq!(map.len(), 2);
}
#[test]
fn test_write_via() {
struct DurationMarker;
struct DurationLike {
secs: u64,
nanos: u32,
}
impl IntoEure<DurationLike> for DurationMarker {
type Error = WriteError;
fn write(value: DurationLike, c: &mut DocumentConstructor) -> Result<(), Self::Error> {
c.record(|rec| {
rec.field("secs", value.secs)?;
rec.field("nanos", value.nanos)?;
Ok::<(), WriteError>(())
})
}
}
let mut c = DocumentConstructor::new();
c.write_via::<DurationMarker, _>(DurationLike {
secs: 60,
nanos: 123,
})
.unwrap();
let doc = c.finish();
let map = doc.root().as_map().unwrap();
assert_eq!(map.len(), 2);
}
#[test]
fn test_array_write() {
let mut c = DocumentConstructor::new();
c.write([1i32, 2, 3]).unwrap();
let doc = c.finish();
let arr = doc.root().as_array().unwrap();
assert_eq!(arr.len(), 3);
}
#[test]
fn test_array_empty_write() {
let mut c = DocumentConstructor::new();
let empty: [i32; 0] = [];
c.write(empty).unwrap();
let doc = c.finish();
let arr = doc.root().as_array().unwrap();
assert_eq!(arr.len(), 0);
}
#[test]
fn test_array_roundtrip() {
let original: [i32; 3] = [10, 20, 30];
let mut c = DocumentConstructor::new();
c.write(original).unwrap();
let doc = c.finish();
let root_id = doc.get_root_id();
let parsed: [i32; 3] = doc.parse(root_id).unwrap();
assert_eq!(parsed, original);
}
#[test]
fn test_regex_roundtrip() {
let original = regex::Regex::new(r"^[a-z]+\d{2,4}$").unwrap();
let mut c = DocumentConstructor::new();
c.write(original.clone()).unwrap();
let doc = c.finish();
let root_id = doc.get_root_id();
let parsed: regex::Regex = doc.parse(root_id).unwrap();
assert_eq!(parsed.as_str(), original.as_str());
}
#[test]
fn test_cow_borrowed_str() {
let mut c = DocumentConstructor::new();
let value: Cow<'_, str> = Cow::Borrowed("hello");
c.write(value).unwrap();
let doc = c.finish();
assert_eq!(
doc.root().content,
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
);
}
#[test]
fn test_cow_owned_str() {
let mut c = DocumentConstructor::new();
let value: Cow<'static, str> = Cow::Owned("hello".to_string());
c.write(value).unwrap();
let doc = c.finish();
assert_eq!(
doc.root().content,
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("hello")))
);
}
#[test]
fn test_document_write_primitive() {
let src = eure!({ = "hello" });
let mut c = DocumentConstructor::new();
c.write(src.clone()).unwrap();
let result = c.finish();
assert_eq!(result, src);
}
#[test]
fn test_document_write_map() {
let src = eure!({
name = "Alice"
age = 30
});
let mut c = DocumentConstructor::new();
c.write(src.clone()).unwrap();
let result = c.finish();
assert_eq!(result, src);
}
#[test]
fn test_document_write_array() {
let src = eure!({ = [1, 2, 3] });
let mut c = DocumentConstructor::new();
c.write(src.clone()).unwrap();
let result = c.finish();
assert_eq!(result, src);
}
#[test]
fn test_document_write_nested() {
let src = eure!({
name = "Alice"
address {
city = "Tokyo"
zip = "100-0001"
}
tags = ["a", "b"]
});
let mut c = DocumentConstructor::new();
c.write(src.clone()).unwrap();
let result = c.finish();
assert_eq!(result, src);
}
#[test]
fn test_document_write_as_field() {
let inner = eure!({ city = "Tokyo" });
let mut c = DocumentConstructor::new();
c.record(|rec| {
rec.field("name", "Alice")?;
rec.field("address", inner)?;
Ok::<(), WriteError>(())
})
.unwrap();
let result = c.finish();
let expected = eure!({
name = "Alice"
address { city = "Tokyo" }
});
assert_eq!(result, expected);
}
#[test]
fn test_document_write_subtree() {
let src = eure!({
name = "Alice"
active = true
});
let mut c = DocumentConstructor::new();
c.write_subtree(&src, src.get_root_id()).unwrap();
let result = c.finish();
assert_eq!(result, src);
}
}