Skip to main content

ion_rs/lazy/encoder/
write_as_ion.rs

1//! Defines traits that allow Rust values to be serialized as Ion.
2//!
3//! In order for a [`LazyRawWriter`](crate::lazy::encoder::LazyRawWriter) to serialize a Rust value
4//! as Ion, that Rust type must implement [`WriteAsIon`] and may optionally also
5//! implement [`WriteAsIon`].
6//!
7//! [`WriteAsIon`] allows the implementor to map the Rust value to an unannotated Ion value.
8//!
9//! [`WriteAsIon`] builds on [`WriteAsIon`], offering a staged writer that requires the
10//! implementor to specify what annotations to write before delegating to [`WriteAsIon`]
11//! to serialize the value itself.
12//!
13//! Types that do not explicitly implement [`WriteAsIon`] will fall back to a blanket implementation
14//! that uses an empty annotations sequence. A custom annotations sequence can be set on a per-value
15//! basis by using the [`annotate`](crate::lazy::encoder::annotate::Annotatable::annotated_with) method
16//! provided by the [`Annotate`](crate::lazy::encoder::annotate::Annotatable) trait.
17use std::io;
18use std::marker::PhantomData;
19
20use crate::lazy::decoder::{Decoder, LazyRawValueExpr, RawValueExpr};
21use crate::lazy::encoder::annotation_seq::AnnotationsVec;
22use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter, ValueWriter};
23use crate::lazy::encoding::Encoding;
24use crate::lazy::expanded::macro_evaluator::RawEExpression;
25use crate::lazy::text::raw::v1_1::arg_group::{EExpArg, EExpArgExpr};
26use crate::lazy::value::LazyValue;
27use crate::lazy::value_ref::ValueRef;
28use crate::v1_0::RawValueRef;
29use crate::{
30    Blob, Clob, Decimal, Element, Int, IonResult, IonType, LazyList, LazyRawFieldExpr,
31    LazyRawFieldName, LazyRawSequence, LazyRawStruct, LazyRawValue, LazySExp, LazyStruct, Null,
32    RawSymbolRef, Symbol, SymbolRef, Timestamp, Value, WriteConfig,
33};
34
35/// Defines how a Rust type should be serialized as Ion in terms of the methods available
36/// on [`ValueWriter`].
37pub trait WriteAsIon {
38    /// Maps this value to the Ion data model using the provided [`ValueWriter`] implementation.
39    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()>;
40
41    /// Encodes this value as an Ion stream with `self` as the single top-level value.
42    /// If the requested encoding is binary of any version, returns a `Vec<u8>` containing the
43    /// encoded bytes. If the requested encoding is text of any version, returns a `String` instead.
44    /// ```
45    ///# use ion_rs::IonResult;
46    ///# #[cfg(feature = "experimental-reader-writer")]
47    ///# fn main() -> IonResult<()> {
48    ///# use ion_rs::*;
49    ///
50    /// use ion_rs::WriteAsIon;
51    /// let ion_text: String = "foo bar baz".encode_as(v1_0::Text)?;
52    /// let element = Element::read_one(ion_text)?;
53    /// assert_eq!(element.as_string().unwrap(), "foo bar baz");
54    ///# Ok(())
55    ///# }
56    ///# #[cfg(not(feature = "experimental-reader-writer"))]
57    ///# fn main() -> IonResult<()> { Ok(()) }
58    /// ```
59    fn encode_as<E: Encoding, C: Into<WriteConfig<E>>>(&self, config: C) -> IonResult<E::Output>
60    where
61        for<'a> &'a Self: WriteAsIon,
62    {
63        config.into().encode(self)
64    }
65
66    /// Encodes this value as an Ion stream with `self` as the single top-level value, writing
67    /// the resulting stream to the specified `output`.
68    fn encode_to<E: Encoding, C: Into<WriteConfig<E>>, W: io::Write>(
69        &self,
70        config: C,
71        output: W,
72    ) -> IonResult<W>
73    where
74        for<'a> &'a Self: WriteAsIon,
75    {
76        config.into().encode_to(self, output)
77    }
78}
79
80impl WriteAsIon for Element {
81    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
82        if self.annotations().is_empty() {
83            self.value().write_as_ion(writer)
84        } else {
85            self.value()
86                .write_as_ion(writer.with_annotations(self.annotations().as_ref())?)
87        }
88    }
89}
90
91// ===== WriteAsIonValue implementations for common types =====
92
93macro_rules! impl_write_as_ion_value {
94    // End of iteration
95    () => {};
96    // The caller defined an expression to write other than `self` (e.g. `*self`, `*self.0`, etc)
97    ($target_type:ty => $method:ident with $self:ident as $value:expr, $($rest:tt)*) => {
98        impl WriteAsIon for $target_type {
99            #[inline]
100            fn write_as_ion<V: ValueWriter>(&$self, writer: V) -> IonResult<()> {
101                writer.$method($value)
102            }
103        }
104        impl_write_as_ion_value!($($rest)*);
105    };
106    // We're writing the expression `self`
107    ($target_type:ty => $method:ident, $($rest:tt)*) => {
108        impl WriteAsIon for $target_type {
109            #[inline]
110            fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
111                writer.$method(self)
112            }
113        }
114        impl_write_as_ion_value!($($rest)*);
115    };
116}
117
118// TODO: For the moment, `i8` and `u8` do not directly implement `WriteAsIon` because this causes
119//       the desired serialization for `&[u8]` and `&[i8]` to be ambiguous. They could be serialized
120//       either as blobs or as lists of integers. We should use the same trick that `SExpTypeHint`
121//       employs to make it possible for users to override the default blob serialization by writing:
122//           writer.write(&[1u8, 2, 3].as_list())
123impl_write_as_ion_value!(
124    Null => write_null with self as self.0,
125    bool => write_bool with self as *self,
126    i16 => write_i64 with self as *self as i64,
127    i32 => write_i64 with self as *self as i64,
128    i64 => write_i64 with self as *self,
129    isize => write_i64 with self as *self as i64,
130    u16 => write_i64 with self as i64::from(*self),
131    u32 => write_i64 with self as i64::from(*self),
132    u64 => write_int with self as &Int::from(*self),
133    usize => write_int with self as &Int::from(*self),
134    f32 => write_f32 with self as *self,
135    f64 => write_f64 with self as *self,
136    Int => write_int,
137    Decimal => write_decimal,
138    Timestamp => write_timestamp,
139    Symbol => write_symbol,
140    &str => write_string,
141    String => write_string,
142    &[u8] => write_blob,
143    Blob => write_blob,
144    Clob => write_clob,
145);
146
147impl WriteAsIon for RawSymbolRef<'_> {
148    #[inline]
149    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
150        writer.write_symbol(self)
151    }
152}
153
154impl WriteAsIon for SymbolRef<'_> {
155    #[inline]
156    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
157        writer.write_symbol(self)
158    }
159}
160
161impl<const N: usize> WriteAsIon for [u8; N] {
162    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
163        writer.write_blob(self)
164    }
165}
166
167impl<T: WriteAsIon> WriteAsIon for &T {
168    #[inline]
169    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
170        (*self).write_as_ion(writer)
171    }
172}
173
174macro_rules! impl_write_as_ion_value_for_iterable {
175    ($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => {
176        impl<'a, $item $(, const $n: $n_type)?> WriteAsIon for $iterable
177        where
178            $item: WriteAsIon + 'a,
179        {
180            fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
181                writer.write_list(self.into_iter())
182            }
183        }
184    };
185}
186
187impl_write_as_ion_value_for_iterable!(Vec<T>, T);
188impl_write_as_ion_value_for_iterable!(&[T], T);
189impl_write_as_ion_value_for_iterable!([T; N], T, const N: usize);
190
191// This code is not used within the library but is intentionally available to tests and applications.
192#[allow(dead_code)]
193pub trait WriteAsSExp<T>: Sized
194where
195    T: WriteAsIon,
196{
197    // The name `as_sexp` makes common cases read as a short sentence:
198    //     writer.write([1, 2, 3].as_sexp())?;
199    // Clippy complains because in most contexts, `as_*` methods borrow by reference.
200    #[allow(clippy::wrong_self_convention)]
201    /// Wraps `self` (which may be a reference) in a [`SExpTypeHint`], causing the value to be
202    /// serialized as an Ion S-expression instead of a list.
203    fn as_sexp(self) -> SExpTypeHint<Self, T>;
204}
205macro_rules! impl_write_as_sexp_for_iterable {
206    ($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => {
207        impl<$item $(, const $n: $n_type)?> WriteAsSExp<$item> for $iterable
208        where
209            $item: WriteAsIon,
210        {
211            fn as_sexp(self) -> SExpTypeHint<Self, T> {
212                SExpTypeHint::new(self)
213            }
214        }
215    };
216}
217
218impl_write_as_sexp_for_iterable!(Vec<T>, T);
219impl_write_as_sexp_for_iterable!(&[T], T);
220impl_write_as_sexp_for_iterable!([T; N], T, const N: usize);
221
222/// A wrapper type that hints to the Ion writer that the sequence within should be serialized
223/// as an s-expression, not a list.
224///
225/// Slices, `Vec`s, and arrays all implement the [`WriteAsSExp`] trait, which grants them the
226/// [`as_sexp()`](WriteAsSExp::as_sexp) method.
227pub struct SExpTypeHint<S, T> {
228    values: S,
229    spooky: PhantomData<T>,
230}
231
232#[allow(dead_code)] // TODO: Evaluate
233impl<S, T> SExpTypeHint<S, T> {
234    pub fn new(values: S) -> Self {
235        Self {
236            values,
237            spooky: PhantomData,
238        }
239    }
240}
241
242macro_rules! impl_write_as_ion_value_for_sexp_type_hint {
243    ($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => {
244        impl<$item $(, const $n: $n_type)?> WriteAsIon for SExpTypeHint<$iterable, $item>
245        where
246            $item: WriteAsIon,
247            for<'a> &'a $item: WriteAsIon,
248        {
249            fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
250                writer.write_sexp((&self.values).into_iter())
251            }
252        }
253    };
254}
255
256impl_write_as_ion_value_for_sexp_type_hint!(Vec<T>, T);
257impl_write_as_ion_value_for_sexp_type_hint!(&[T], T);
258impl_write_as_ion_value_for_sexp_type_hint!([T; N], T, const N: usize);
259
260impl WriteAsIon for Value {
261    fn write_as_ion<V: ValueWriter>(&self, value_writer: V) -> IonResult<()> {
262        use Value::*;
263        match self {
264            Null(i) => value_writer.write_null(*i),
265            Bool(b) => value_writer.write_bool(*b),
266            Int(i) => value_writer.write_int(i),
267            Float(f) => value_writer.write_f64(*f),
268            Decimal(d) => value_writer.write_decimal(d),
269            Timestamp(t) => value_writer.write_timestamp(t),
270            Symbol(s) => value_writer.write_symbol(s),
271            String(s) => value_writer.write_string(s),
272            Clob(c) => value_writer.write_clob(c),
273            Blob(b) => value_writer.write_blob(b),
274            List(l) => value_writer.write_list(l),
275            SExp(s) => value_writer.write_sexp(s),
276            Struct(s) => value_writer.write_struct(s.iter()),
277        }
278    }
279}
280
281impl<D: Decoder> WriteAsIon for LazyValue<'_, D> {
282    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
283        if self.has_annotations() {
284            let mut annotations = AnnotationsVec::new();
285            for annotation in self.annotations() {
286                annotations.push(annotation?.into());
287            }
288            self.read()?
289                .write_as_ion(writer.with_annotations(annotations)?)
290        } else {
291            self.read()?.write_as_ion(writer)
292        }
293    }
294}
295
296impl<D: Decoder> WriteAsIon for RawValueRef<'_, D> {
297    fn write_as_ion<V: ValueWriter>(&self, value_writer: V) -> IonResult<()> {
298        use RawValueRef::*;
299        match self {
300            Null(i) => value_writer.write_null(*i),
301            Bool(b) => value_writer.write_bool(*b),
302            Int(i) => value_writer.write_int(i),
303            Float(f) => value_writer.write_f64(*f),
304            Decimal(d) => value_writer.write_decimal(d),
305            Timestamp(t) => value_writer.write_timestamp(t),
306            Symbol(s) => value_writer.write_symbol(s),
307            String(s) => value_writer.write_string(s.text()),
308            Clob(c) => value_writer.write_clob(c.data()),
309            Blob(b) => value_writer.write_blob(b.data()),
310            List(l) => {
311                let mut list_writer = value_writer.list_writer()?;
312                for value_result in l.iter() {
313                    list_writer.write(WriteableRawValueExpr::<'_, D>::new(value_result?))?;
314                }
315                list_writer.close()
316            }
317            SExp(s) => {
318                let mut sexp_writer = value_writer.sexp_writer()?;
319                for value_result in s.iter() {
320                    sexp_writer.write(WriteableRawValueExpr::<'_, D>::new(value_result?))?;
321                }
322                sexp_writer.close()
323            }
324            Struct(s) => {
325                let mut struct_writer = value_writer.struct_writer()?;
326                for field_result in s.iter() {
327                    let field: LazyRawFieldExpr<'_, D> = field_result?;
328                    match field {
329                        LazyRawFieldExpr::NameValue(name, value) => {
330                            struct_writer.write(name.read()?, WriteableRawValue::new(value))?;
331                        }
332                        LazyRawFieldExpr::NameEExp(name, eexp) => {
333                            struct_writer.write(name.read()?, WriteableEExp::new(eexp))?;
334                        }
335                        LazyRawFieldExpr::EExp(_eexp) => {
336                            todo!("Writing e-expressions in field name position during transcription.");
337                        }
338                    }
339                }
340                struct_writer.close()
341            }
342        }
343    }
344}
345
346/// Wrapper type for `LazyRawValue`s that implements `WriteAsIon`.
347pub struct WriteableRawValue<'a, D: Decoder, RawValue: LazyRawValue<'a, D>> {
348    raw_value: RawValue,
349    spooky: PhantomData<&'a D>,
350}
351
352impl<'a, D: Decoder, RawValue: LazyRawValue<'a, D>> WriteableRawValue<'a, D, RawValue> {
353    pub fn new(raw_value: RawValue) -> Self {
354        Self {
355            raw_value,
356            spooky: PhantomData,
357        }
358    }
359}
360
361impl<'a, D: Decoder, RawValue: LazyRawValue<'a, D>> WriteAsIon
362    for WriteableRawValue<'a, D, RawValue>
363{
364    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
365        if self.raw_value.has_annotations() {
366            let mut annotations = AnnotationsVec::new();
367            for annotation in self.raw_value.annotations() {
368                annotations.push(annotation?);
369            }
370            self.raw_value
371                .read()?
372                .write_as_ion(writer.with_annotations(annotations)?)
373        } else {
374            self.raw_value.read()?.write_as_ion(writer)
375        }
376    }
377}
378
379/// Wrapper type for `RawEExpression`s that implements `WriteAsIon`.
380pub struct WriteableEExp<'a, D: Decoder<EExp<'a> = RawEExp>, RawEExp: RawEExpression<'a, D> + 'a> {
381    raw_eexp: RawEExp,
382    spooky: PhantomData<&'a D>,
383}
384
385impl<'a, D: Decoder<EExp<'a> = RawEExp>, RawEExp: RawEExpression<'a, D> + 'a>
386    WriteableEExp<'a, D, RawEExp>
387{
388    pub fn new(raw_eexp: RawEExp) -> Self {
389        Self {
390            raw_eexp,
391            spooky: PhantomData,
392        }
393    }
394}
395
396impl<'a, D: Decoder<EExp<'a> = RawEExp>, RawEExp: RawEExpression<'a, D> + 'a> WriteAsIon
397    for WriteableEExp<'a, D, RawEExp>
398{
399    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
400        let id = self.raw_eexp.id();
401        let mut eexp_writer = writer.eexp_writer(id)?;
402        for arg_result in self.raw_eexp.raw_arguments() {
403            let arg = arg_result?;
404            eexp_writer.write(WriteableEExpArg::<'_, D>::new(arg))?;
405        }
406        eexp_writer.close()
407    }
408}
409
410/// Wrapper type for `EExpArg`s that implements `WriteAsIon`.
411pub struct WriteableEExpArg<'a, D: Decoder> {
412    arg_expr: EExpArg<'a, D>,
413    spooky: PhantomData<&'a D>,
414}
415
416impl<'a, D: Decoder> WriteableEExpArg<'a, D> {
417    pub fn new(arg_expr: EExpArg<'a, D>) -> Self {
418        Self {
419            arg_expr,
420            spooky: PhantomData,
421        }
422    }
423}
424
425impl<D: Decoder> WriteAsIon for WriteableEExpArg<'_, D> {
426    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
427        use EExpArgExpr::*;
428        match self.arg_expr.expr() {
429            // TODO: Untagged encodings
430            ValueLiteral(v) => WriteableRawValue::new(*v).write_as_ion(writer),
431            EExp(e) => WriteableEExp::new(*e).write_as_ion(writer),
432            ArgGroup(group) => WriteableEExpArgGroup::<'_, D>::new(*group).write_as_ion(writer),
433        }
434    }
435}
436
437/// Wrapper type for `WriteableEExpArgGroup`s that implements `WriteAsIon`.
438// This is not yet implemented.
439#[allow(dead_code)]
440pub struct WriteableEExpArgGroup<'a, D: Decoder> {
441    arg_group: <<D as Decoder>::EExp<'a> as RawEExpression<'a, D>>::ArgGroup,
442    spooky: PhantomData<&'a D>,
443}
444
445impl<'a, D: Decoder> WriteableEExpArgGroup<'a, D> {
446    pub fn new(arg_group: <<D as Decoder>::EExp<'a> as RawEExpression<'a, D>>::ArgGroup) -> Self {
447        Self {
448            arg_group,
449            spooky: PhantomData,
450        }
451    }
452}
453
454impl<D: Decoder> WriteAsIon for WriteableEExpArgGroup<'_, D> {
455    fn write_as_ion<V: ValueWriter>(&self, _writer: V) -> IonResult<()> {
456        todo!()
457    }
458}
459
460/// Wrapper type for `LazyRawValueExpr`s that implements `WriteAsIon`.
461pub struct WriteableRawValueExpr<'a, D: Decoder> {
462    raw_value_expr: LazyRawValueExpr<'a, D>,
463    spooky: PhantomData<&'a D>,
464}
465
466impl<'a, D: Decoder> WriteableRawValueExpr<'a, D> {
467    pub fn new(raw_value_expr: LazyRawValueExpr<'a, D>) -> Self {
468        Self {
469            raw_value_expr,
470            spooky: PhantomData,
471        }
472    }
473}
474
475impl<D: Decoder> WriteAsIon for WriteableRawValueExpr<'_, D> {
476    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
477        use RawValueExpr::*;
478        match self.raw_value_expr {
479            ValueLiteral(v) => WriteableRawValue::new(v).write_as_ion(writer),
480            EExp(e) => WriteableEExp::new(e).write_as_ion(writer),
481        }
482    }
483}
484
485impl<D: Decoder> WriteAsIon for ValueRef<'_, D> {
486    fn write_as_ion<V: ValueWriter>(&self, value_writer: V) -> IonResult<()> {
487        use ValueRef::*;
488        match self {
489            Null(i) => value_writer.write_null(*i),
490            Bool(b) => value_writer.write_bool(*b),
491            Int(i) => value_writer.write_int(i),
492            Float(f) => value_writer.write_f64(*f),
493            Decimal(d) => value_writer.write_decimal(d),
494            Timestamp(t) => value_writer.write_timestamp(t),
495            Symbol(s) => value_writer.write_symbol(s),
496            String(s) => value_writer.write_string(s.text()),
497            Clob(c) => value_writer.write_clob(c.data()),
498            Blob(b) => value_writer.write_blob(b.data()),
499            List(l) => value_writer.write(l),
500            SExp(s) => value_writer.write(s),
501            Struct(s) => value_writer.write(s),
502        }
503    }
504}
505
506impl<D: Decoder> WriteAsIon for LazyList<'_, D> {
507    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
508        let mut list = writer.list_writer()?;
509        for value in self {
510            list.write(value?)?;
511        }
512        list.close()
513    }
514}
515
516impl<D: Decoder> WriteAsIon for LazySExp<'_, D> {
517    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
518        let mut sexp = writer.sexp_writer()?;
519        for value in self {
520            sexp.write(value?)?;
521        }
522        sexp.close()
523    }
524}
525
526impl<D: Decoder> WriteAsIon for LazyStruct<'_, D> {
527    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
528        let mut struct_writer = writer.struct_writer()?;
529        for field_result in self {
530            let field = field_result?;
531            struct_writer.write(field.name()?, field.value())?;
532        }
533        struct_writer.close()
534    }
535}
536
537impl<T: WriteAsIon> WriteAsIon for Option<T> {
538    fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
539        if let Some(value) = self {
540            value.write_as_ion(writer)
541        } else {
542            writer.write_null(IonType::Null)
543        }
544    }
545}
546
547#[cfg(test)]
548mod tests {
549    use super::*;
550    use crate::v1_0;
551
552    #[test]
553    fn test_element_write_as_ion_without_annotations() -> IonResult<()> {
554        let element = Element::read_one("42")?;
555        let encoded: String = element.encode_as(v1_0::Text)?;
556        assert_eq!(encoded.trim(), "42");
557        Ok(())
558    }
559
560    #[test]
561    fn test_element_write_as_ion_with_annotations() -> IonResult<()> {
562        let element = Element::read_one("foo::42")?;
563        let encoded: String = element.encode_as(v1_0::Text)?;
564        assert_eq!(encoded.trim(), "foo::42");
565        Ok(())
566    }
567}