ion_rs/element/
writer.rs

1// Copyright Amazon.com, Inc. or its affiliates.
2
3//! Provides utility to serialize Ion data from [`Element`](super::Element) into common targets
4//! such as byte buffers or files.
5
6use crate::result::IonResult;
7
8use crate::element::{Element, Value};
9use crate::{IonType, IonWriter};
10pub use Format::*;
11pub use TextKind::*;
12
13/// Serializes [`Element`] instances into some kind of output sink.
14pub trait ElementWriter {
15    /// Serializes a single [`Element`] as a top-level value.
16    fn write_element(&mut self, element: &Element) -> IonResult<()>;
17
18    /// Serializes a collection of [`Element`] as a series of top-level values.
19    ///
20    /// This will return [`Err`] if writing any element causes a failure.
21    fn write_elements<'a, I: IntoIterator<Item = &'a Element>>(
22        &'a mut self,
23        elements: I,
24    ) -> IonResult<()> {
25        for element in elements.into_iter() {
26            self.write_element(element)?;
27        }
28        Ok(())
29    }
30}
31
32impl<W> ElementWriter for W
33where
34    W: IonWriter,
35{
36    fn write_element(&mut self, element: &Element) -> IonResult<()> {
37        self.set_annotations(element.annotations());
38
39        match element.value() {
40            Value::Null(ion_type) => self.write_null(*ion_type),
41            Value::Int(i) => self.write_int(i),
42            Value::Float(f) => {
43                let f = *f;
44                let small_float = f as f32;
45                if (small_float as f64) == f {
46                    self.write_f32(small_float)
47                } else {
48                    self.write_f64(f)
49                }
50            }
51            Value::Decimal(d) => self.write_decimal(d),
52            Value::Timestamp(t) => self.write_timestamp(t),
53            Value::String(s) => self.write_string(s),
54            Value::Symbol(s) => self.write_symbol(s),
55            Value::Bool(b) => self.write_bool(*b),
56            Value::Blob(b) => self.write_blob(b),
57            Value::Clob(c) => self.write_clob(c),
58            Value::SExp(s) => {
59                self.step_in(IonType::SExp)?;
60                self.write_elements(s.elements())?;
61                self.step_out()
62            }
63            Value::List(l) => {
64                self.step_in(IonType::List)?;
65                self.write_elements(l.elements())?;
66                self.step_out()
67            }
68            Value::Struct(s) => {
69                self.step_in(IonType::Struct)?;
70                for (name, value) in s.fields() {
71                    self.set_field_name(name);
72                    self.write_element(value)?;
73                }
74                self.step_out()
75            }
76        }
77    }
78}
79
80/// Whether or not the text is pretty printed or serialized in a more compact representation.
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
82pub enum TextKind {
83    Compact,
84    Lines,
85    Pretty,
86}
87
88/// Basic configuration options for [`ElementWriter`] instances.
89#[derive(Copy, Clone, Debug, Eq, PartialEq)]
90pub enum Format {
91    Text(TextKind),
92    Binary,
93    // TODO a mode for Json(TextKind)
94}
95
96#[cfg(test)]
97mod tests {
98    use crate::element::writer::ElementWriter;
99    use crate::element::Element;
100    use crate::ion_data::IonEq;
101    use crate::text::text_writer::TextWriterBuilder;
102
103    use crate::{IonResult, IonWriter};
104    use nom::AsBytes;
105
106    #[test]
107    fn element_roundtrip() -> IonResult<()> {
108        let mut buffer = Vec::new();
109        let mut writer = TextWriterBuilder::default().build(&mut buffer)?;
110
111        let ion = r#"
112            null true 0 1e0 2.0 2022T foo "bar" (foo bar baz) [foo, bar, baz] {foo: true, bar: false}
113        "#;
114        let expected_elements = Element::read_all(ion.as_bytes())?;
115        writer.write_elements(&expected_elements)?;
116        writer.flush()?;
117        let actual_elements = Element::read_all(writer.output().as_bytes())?;
118        assert!(expected_elements.ion_eq(&actual_elements));
119        Ok(())
120    }
121}