Skip to content

Commit 06fe5f8

Browse files
committed
adds suggested changes
* Modifies element APIs to use raw text/bianry writers instead of lazy writer * Adds implementation of `WriteAsIon` for `Element` and implementation of `WriteAsIonValue` for `Value` * adds build API implementation for binary and text lazy writers * Puts `WriteConfig` under `experimental-lazy-reader` as it uses `Encoding`
1 parent fdc54cf commit 06fe5f8

File tree

7 files changed

+219
-69
lines changed

7 files changed

+219
-69
lines changed

src/element/mod.rs

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@
1212
//! [simd-json-value]: https://docs.rs/simd-json/latest/simd_json/value/index.html
1313
//! [serde-json-value]: https://docs.serde.rs/serde_json/value/enum.Value.html
1414
15+
use crate::binary::binary_writer::BinaryWriterBuilder;
1516
use crate::element::builders::{SequenceBuilder, StructBuilder};
1617
use crate::element::reader::ElementReader;
1718
use crate::ion_data::{IonEq, IonOrd};
18-
#[cfg(feature = "experimental-writer")]
1919
use crate::ion_writer::IonWriter;
2020
use crate::text::text_formatter::IonValueFormatter;
21-
use crate::{ion_data, Decimal, Int, IonError, IonResult, IonType, Str, Symbol, Timestamp};
22-
23-
#[cfg(feature = "experimental-writer")]
24-
use crate::TextKind;
21+
use crate::text::text_writer::TextWriterBuilder;
22+
use crate::{
23+
ion_data, Decimal, Format, Int, IonError, IonResult, IonType, Str, Symbol, TextKind, Timestamp,
24+
};
2525
use std::cmp::Ordering;
2626
use std::fmt::{Display, Formatter};
27+
use std::io;
2728

2829
mod annotations;
2930
pub(crate) mod iterators;
@@ -40,14 +41,11 @@ mod sequence;
4041

4142
// Re-export the Value variant types and traits so they can be accessed directly from this module.
4243
use crate::data_source::IonDataSource;
43-
#[cfg(feature = "experimental-writer")]
44-
use crate::element::writer::WriteConfig;
45-
#[cfg(feature = "experimental-writer")]
46-
use crate::lazy::encoding::{BinaryEncoding_1_0, TextEncoding_1_0};
44+
use crate::element::writer::ElementWriter;
4745
use crate::reader::ReaderBuilder;
48-
use crate::result::IonFailure;
49-
use crate::ElementWriter;
5046
use crate::{Blob, Bytes, Clob, List, SExp, Struct};
47+
48+
use crate::result::IonFailure;
5149
pub use annotations::{Annotations, IntoAnnotations};
5250
pub use sequence::Sequence;
5351

@@ -751,6 +749,104 @@ impl Element {
751749
Ok(())
752750
}
753751

752+
#[doc = r##"
753+
Serializes this [`Element`] as Ion, writing the resulting bytes to the provided [`io::Write`].
754+
The caller must verify that `output` is either empty or only contains Ion of the same
755+
format (text or binary) before writing begins.
756+
757+
This method constructs a new writer for each invocation, which means that there will only be a single
758+
top level value in the output stream. Writing several values to the same stream is preferable to
759+
maximize encoding efficiency. See [`write_all_as`](Self::write_all_as) for details.
760+
"##]
761+
#[cfg_attr(
762+
feature = "experimental-writer",
763+
doc = r##"
764+
To reuse a writer and have greater control over resource
765+
management, see [`Element::write_to`].
766+
"##
767+
)]
768+
#[doc = r##"
769+
```
770+
# use ion_rs::{Format, IonResult, TextKind};
771+
# fn main() -> IonResult<()> {
772+
use ion_rs::Element;
773+
use ion_rs::ion_list;
774+
775+
// Construct an Element
776+
let element_before: Element = ion_list! [1, 2, 3].into();
777+
778+
// Write the Element to a buffer using a specified format
779+
let mut buffer = Vec::new();
780+
element_before.write_as(Format::Text(TextKind::Pretty), &mut buffer)?;
781+
782+
// Read the Element back from the serialized form
783+
let element_after = Element::read_one(&buffer)?;
784+
785+
// Confirm that no data was lost
786+
assert_eq!(element_before, element_after);
787+
# Ok(())
788+
# }
789+
```
790+
"##]
791+
pub fn write_as<W: io::Write>(&self, format: Format, output: W) -> IonResult<()> {
792+
match format {
793+
Format::Text(text_kind) => {
794+
let mut text_writer = TextWriterBuilder::new(text_kind).build(output)?;
795+
Element::write_element_to(self, &mut text_writer)?;
796+
text_writer.flush()
797+
}
798+
Format::Binary => {
799+
let mut binary_writer = BinaryWriterBuilder::default().build(output)?;
800+
Element::write_element_to(self, &mut binary_writer)?;
801+
binary_writer.flush()
802+
}
803+
}
804+
}
805+
806+
/// Serializes each of the provided [`Element`]s as Ion, writing the resulting bytes to the
807+
/// provided [`io::Write`]. The caller must verify that `output` is either empty or only
808+
/// contains Ion of the same format (text or binary) before writing begins.
809+
///
810+
/// This method is preferable to [`write_as`](Self::write_as) when writing streams consisting
811+
/// of more than one top-level value; the writer can re-use the same symbol table definition
812+
/// to encode each value, resulting in a more compact representation.
813+
///
814+
/// ```
815+
///# use ion_rs::IonResult;
816+
///# fn main() -> IonResult<()> {
817+
/// use ion_rs::{Element, Format, ion_seq};
818+
///
819+
/// let elements = ion_seq!("foo", "bar", "baz");
820+
/// let mut buffer: Vec<u8> = Vec::new();
821+
/// Element::write_all_as(&elements, Format::Binary, &mut buffer)?;
822+
/// let roundtrip_elements = Element::read_all(buffer)?;
823+
/// assert_eq!(elements, roundtrip_elements);
824+
///# Ok(())
825+
///# }
826+
/// ```
827+
pub fn write_all_as<'a, W: io::Write, I: IntoIterator<Item = &'a Element>>(
828+
elements: I,
829+
format: Format,
830+
output: W,
831+
) -> IonResult<()> {
832+
match format {
833+
Format::Text(text_kind) => {
834+
let mut text_writer = TextWriterBuilder::new(text_kind).build(output)?;
835+
for element in elements {
836+
Element::write_element_to(element, &mut text_writer)?;
837+
}
838+
text_writer.flush()
839+
}
840+
Format::Binary => {
841+
let mut binary_writer = BinaryWriterBuilder::default().build(output)?;
842+
for element in elements {
843+
Element::write_element_to(element, &mut binary_writer)?;
844+
}
845+
binary_writer.flush()
846+
}
847+
}
848+
}
849+
754850
#[doc = r##"
755851
Serializes this [`Element`] as binary Ion, returning the output as a `Vec<u8>`.
756852
"##]
@@ -785,15 +881,10 @@ assert_eq!(element_before, element_after);
785881
# }
786882
```
787883
"##]
788-
#[cfg(feature = "experimental-writer")]
789884
pub fn to_binary(&self) -> IonResult<Vec<u8>> {
790-
let buffer = Vec::new();
791-
let write_config: WriteConfig<BinaryEncoding_1_0> =
792-
WriteConfig::<BinaryEncoding_1_0>::new();
793-
let mut writer = write_config.build(buffer)?;
794-
Element::write_element_to(self, &mut writer)?;
795-
writer.flush()?;
796-
Ok(writer.output().to_owned().to_owned())
885+
let mut buffer = Vec::new();
886+
self.write_as(Format::Binary, &mut buffer)?;
887+
Ok(buffer)
797888
}
798889

799890
#[doc = r##"
@@ -832,15 +923,10 @@ assert_eq!(element_before, element_after);
832923
# }
833924
```
834925
"##]
835-
#[cfg(feature = "experimental-writer")]
836926
pub fn to_text(&self, text_kind: TextKind) -> IonResult<String> {
837-
let buffer = Vec::new();
838-
let write_config: WriteConfig<TextEncoding_1_0> =
839-
WriteConfig::<TextEncoding_1_0>::new(text_kind);
840-
let mut writer = write_config.build(buffer)?;
841-
Element::write_element_to(self, &mut writer)?;
842-
writer.flush()?;
843-
Ok(std::str::from_utf8(writer.output())
927+
let mut buffer = Vec::new();
928+
self.write_as(Format::Text(text_kind), &mut buffer)?;
929+
Ok(std::str::from_utf8(&buffer)
844930
.expect("writer produced invalid utf-8")
845931
.to_string())
846932
}

src/element/writer.rs

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,56 @@
33
//! Provides utility to serialize Ion data from [`Element`] into common targets
44
//! such as byte buffers or files.
55
6-
#[cfg(feature = "experimental-writer")]
7-
use crate::binary::binary_writer::{BinaryWriter, BinaryWriterBuilder};
86
use crate::ion_writer::IonWriter;
9-
#[cfg(feature = "experimental-writer")]
10-
use crate::lazy::encoding::{BinaryEncoding_1_0, Encoding, TextEncoding_1_0};
117
use crate::result::IonResult;
12-
#[cfg(feature = "experimental-writer")]
13-
use crate::text::text_writer::{TextWriter, TextWriterBuilder};
148
pub use crate::Format::*;
159
pub use crate::TextKind::*;
1610
use crate::{Element, IonType, TextKind, Value};
17-
#[cfg(feature = "experimental-writer")]
1811
use std::io;
19-
#[cfg(feature = "experimental-writer")]
2012
use std::marker::PhantomData;
13+
#[cfg(feature = "experimental-lazy-reader")]
14+
use {
15+
crate::lazy::encoder::{LazyEncoder, LazyRawWriter},
16+
crate::lazy::encoding::{BinaryEncoding_1_0, Encoding, TextEncoding_1_0},
17+
};
2118

2219
/// Writer configuration to provide format and Ion version details to writer through encoding
2320
/// This will be used to create a writer without specifying which writer methods to use
24-
#[cfg(feature = "experimental-writer")]
21+
#[cfg(feature = "experimental-lazy-reader")]
2522
pub struct WriteConfig<E: Encoding> {
2623
pub(crate) kind: WriteConfigKind,
2724
phantom_data: PhantomData<E>,
2825
}
2926

30-
#[cfg(feature = "experimental-writer")]
27+
#[cfg(feature = "experimental-lazy-reader")]
28+
impl<E: Encoding + LazyEncoder> WriteConfig<E> {
29+
/// Builds a writer based on writer configuration
30+
pub fn build<W: io::Write>(self, output: W) -> IonResult<<E as LazyEncoder>::Writer<W>> {
31+
E::Writer::build(self, output)
32+
}
33+
}
34+
35+
#[cfg(feature = "experimental-lazy-reader")]
3136
impl WriteConfig<TextEncoding_1_0> {
3237
pub fn new(text_kind: TextKind) -> Self {
3338
Self {
3439
kind: WriteConfigKind::Text(TextWriteConfig { text_kind }),
3540
phantom_data: Default::default(),
3641
}
3742
}
38-
39-
/// Builds a text writer based on writer configuration
40-
pub fn build<W: io::Write>(&self, output: W) -> IonResult<TextWriter<W>> {
41-
match &self.kind {
42-
WriteConfigKind::Text(text_writer_config) => {
43-
TextWriterBuilder::new(text_writer_config.text_kind).build(output)
44-
}
45-
WriteConfigKind::Binary(_) => {
46-
unreachable!("Binary writer can not be created from text encoding")
47-
}
48-
}
49-
}
5043
}
5144

52-
#[cfg(feature = "experimental-writer")]
45+
#[cfg(feature = "experimental-lazy-reader")]
5346
impl WriteConfig<BinaryEncoding_1_0> {
5447
pub fn new() -> Self {
5548
Self {
5649
kind: WriteConfigKind::Binary(BinaryWriteConfig),
5750
phantom_data: Default::default(),
5851
}
5952
}
60-
61-
/// Builds a binary writer based on writer configuration
62-
pub fn build<W: io::Write>(&self, output: W) -> IonResult<BinaryWriter<W>> {
63-
match &self.kind {
64-
WriteConfigKind::Text(_) => {
65-
unreachable!("Text writer can not be created from binary encoding")
66-
}
67-
WriteConfigKind::Binary(_) => BinaryWriterBuilder::default().build(output),
68-
}
69-
}
7053
}
7154

72-
#[cfg(feature = "experimental-writer")]
55+
#[cfg(feature = "experimental-lazy-reader")]
7356
impl Default for WriteConfig<BinaryEncoding_1_0> {
7457
fn default() -> Self {
7558
Self::new()
@@ -82,12 +65,12 @@ pub(crate) enum WriteConfigKind {
8265
Binary(BinaryWriteConfig),
8366
}
8467

85-
/// Text writer configuration with text kind to be sued to create a writer
68+
/// Text writer configuration with text kind to be used to create a writer
8669
pub(crate) struct TextWriteConfig {
8770
text_kind: TextKind,
8871
}
8972

90-
/// Binary writer configuration to be sued to create a writer
73+
/// Binary writer configuration to be used to create a writer
9174
// TODO: Add appropriate binary configuration if required for 1.1
9275
pub(crate) struct BinaryWriteConfig;
9376

src/lazy/encoder/binary/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use bumpalo::Bump as BumpAllocator;
55
use delegate::delegate;
66

77
use crate::binary::var_uint::VarUInt;
8+
use crate::element::writer::{WriteConfig, WriteConfigKind};
89
use crate::lazy::encoder::binary::value_writer::{
910
BinaryAnnotatableValueWriter_1_0, MAX_INLINE_LENGTH,
1011
};
@@ -13,7 +14,7 @@ use crate::lazy::encoder::value_writer::internal::MakeValueWriter;
1314
use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter};
1415
use crate::lazy::encoder::write_as_ion::WriteAsIon;
1516
use crate::lazy::encoder::{LazyEncoder, LazyRawWriter};
16-
use crate::lazy::encoding::BinaryEncoding_1_0;
17+
use crate::lazy::encoding::{BinaryEncoding_1_0, Encoding};
1718
use crate::raw_symbol_token_ref::AsRawSymbolTokenRef;
1819
use crate::result::EncodingError;
1920
use crate::{IonError, IonResult, RawSymbolTokenRef};
@@ -132,6 +133,16 @@ impl<W: Write> LazyRawWriter<W> for LazyRawBinaryWriter_1_0<W> {
132133
Self::new(output)
133134
}
134135

136+
/// Build binary writer based on given writer configuration
137+
fn build<E: Encoding>(config: WriteConfig<E>, output: W) -> IonResult<Self> {
138+
match &config.kind {
139+
WriteConfigKind::Text(_) => {
140+
unreachable!("Text writer can not be created from binary encoding")
141+
}
142+
WriteConfigKind::Binary(_) => LazyRawBinaryWriter_1_0::new(output),
143+
}
144+
}
145+
135146
delegate! {
136147
to self {
137148
fn flush(&mut self) -> IonResult<()>;

src/lazy/encoder/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#![allow(non_camel_case_types)]
22

3-
use crate::IonResult;
43
use std::fmt::Debug;
54
use std::io::Write;
5+
6+
use crate::element::writer::WriteConfig;
7+
use crate::lazy::encoding::Encoding;
8+
use crate::IonResult;
69
use value_writer::SequenceWriter;
710

811
pub mod annotate;
@@ -36,6 +39,9 @@ pub(crate) mod private {
3639
/// An Ion writer without an encoding context (that is: symbol/macro tables).
3740
pub trait LazyRawWriter<W: Write>: SequenceWriter {
3841
fn new(output: W) -> IonResult<Self>
42+
where
43+
Self: Sized;
44+
fn build<E: Encoding>(config: WriteConfig<E>, output: W) -> IonResult<Self>
3945
where
4046
Self: Sized;
4147
fn flush(&mut self) -> IonResult<()>;

src/lazy/encoder/text/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use crate::element::writer::{WriteConfig, WriteConfigKind};
12
use crate::lazy::encoder::text::value_writer::{
23
TextAnnotatableValueWriter_1_0, TextValueWriter_1_0,
34
};
45
use crate::lazy::encoder::value_writer::internal::MakeValueWriter;
56
use crate::lazy::encoder::value_writer::SequenceWriter;
67
use crate::lazy::encoder::write_as_ion::WriteAsIon;
78
use crate::lazy::encoder::{LazyEncoder, LazyRawWriter};
8-
use crate::lazy::encoding::TextEncoding_1_0;
9+
use crate::lazy::encoding::{Encoding, TextEncoding_1_0};
910
use crate::text::raw_text_writer::{WhitespaceConfig, PRETTY_WHITESPACE_CONFIG};
1011
use crate::IonResult;
1112
use delegate::delegate;
@@ -78,6 +79,16 @@ impl<W: Write> LazyRawWriter<W> for LazyRawTextWriter_1_0<W> {
7879
Ok(LazyRawTextWriter_1_0::new(output))
7980
}
8081

82+
/// Build text writer based on given writer configuration
83+
fn build<E: Encoding>(config: WriteConfig<E>, output: W) -> IonResult<Self> {
84+
match &config.kind {
85+
WriteConfigKind::Text(_) => Ok(LazyRawTextWriter_1_0::new(output)),
86+
WriteConfigKind::Binary(_) => {
87+
unreachable!("Binary writer can not be created from text encoding")
88+
}
89+
}
90+
}
91+
8192
// Delegate the trait methods to the inherent methods; this allows a version of these
8293
// methods to be called on the concrete type even when the trait is not in scope.
8394
delegate! {

0 commit comments

Comments
 (0)