Skip to content

Commit

Permalink
[serde] Implement serde Serializer/Deserializer::is_human_readable
Browse files Browse the repository at this point in the history
Will return true for text encodings, false for binary.
is_human_readable is used to determine whether Serialize implementations
should serialize in human-readable form.

Some types have a human-readable form that may be somewhat expensive
to construct, as well as a binary form that is compact and efficient.

Fixes #790
  • Loading branch information
theli-ua committed Jun 19, 2024
1 parent caebdbd commit c769c7d
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/lazy/encoder/binary/v1_0/container_writers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ impl<'value, 'top> MakeValueWriter for BinaryStructWriter_1_0<'value, 'top> {
}

impl<'value, 'top> StructWriter for BinaryStructWriter_1_0<'value, 'top> {
const IS_HUMAN_READABLE: bool = false;

fn close(self) -> IonResult<()> {
self.container_writer.end()
}
Expand Down
2 changes: 2 additions & 0 deletions src/lazy/encoder/binary/v1_0/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ impl<'value, 'top> ValueWriter for BinaryValueWriter_1_0<'value, 'top> {
type StructWriter = BinaryStructWriter_1_0<'value, 'top>;

type EExpWriter = Never;
const IS_HUMAN_READABLE: bool = false;

delegate_value_writer_to_self!();
}
Expand Down Expand Up @@ -411,6 +412,7 @@ impl<'value, 'top> ValueWriter for BinaryAnnotatedValueWriter_1_0<'value, 'top>
type SExpWriter = BinarySExpWriter_1_0<'value, 'top>;
type StructWriter = BinaryStructWriter_1_0<'value, 'top>;

const IS_HUMAN_READABLE: bool = false;
// Ion 1.0
type EExpWriter = Never;

Expand Down
1 change: 1 addition & 0 deletions src/lazy/encoder/binary/v1_1/container_writers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ impl<'value, 'top> MakeValueWriter for BinaryStructWriter_1_1<'value, 'top> {
}

impl<'value, 'top> StructWriter for BinaryStructWriter_1_1<'value, 'top> {
const IS_HUMAN_READABLE: bool = false;
fn close(mut self) -> IonResult<()> {
if let ContainerEncodingKind::Delimited(_) = &mut self.container_writer.encoder {
// Write the FlexSym escape (FlexUInt 0). The container writer can emit the closing
Expand Down
4 changes: 4 additions & 0 deletions src/lazy/encoder/binary/v1_1/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ impl<'value, 'top> ValueWriter for BinaryValueWriter_1_1<'value, 'top> {

type EExpWriter = BinaryEExpWriter_1_1<'value, 'top>;

const IS_HUMAN_READABLE: bool = false;

delegate_value_writer_to_self!();
}

Expand Down Expand Up @@ -767,6 +769,8 @@ impl<'value, 'top> ValueWriter for BinaryAnnotatedValueWriter_1_1<'value, 'top>
type StructWriter = BinaryStructWriter_1_1<'value, 'top>;
type EExpWriter = BinaryEExpWriter_1_1<'value, 'top>;

const IS_HUMAN_READABLE: bool = false;

annotate_and_delegate_1_1!(
IonType => write_null,
bool => write_bool,
Expand Down
5 changes: 5 additions & 0 deletions src/lazy/encoder/text/v1_0/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ impl<'value, W: Write> MakeValueWriter for TextStructWriter_1_0<'value, W> {
}

impl<'value, W: Write> StructWriter for TextStructWriter_1_0<'value, W> {
const IS_HUMAN_READABLE: bool = true;
fn close(self) -> IonResult<()> {
self.end()
}
Expand Down Expand Up @@ -473,6 +474,8 @@ impl<'value, W: Write + 'value> ValueWriter for TextAnnotatedValueWriter_1_0<'va
// Ion 1.0 does not support macros
type EExpWriter = Never;

const IS_HUMAN_READABLE: bool = true;

delegate_value_writer_to!(fallible closure |self_: Self| self_.encode_annotations());
}

Expand All @@ -498,6 +501,8 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> {
type SExpWriter = TextSExpWriter_1_0<'value, W>;
type StructWriter = TextStructWriter_1_0<'value, W>;

const IS_HUMAN_READABLE: bool = true;

// Ion 1.0 does not support macros
type EExpWriter = Never;
fn write_null(mut self, ion_type: IonType) -> IonResult<()> {
Expand Down
4 changes: 4 additions & 0 deletions src/lazy/encoder/text/v1_1/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ impl<'value, W: Write + 'value> ValueWriter for TextValueWriter_1_1<'value, W> {
type StructWriter = TextStructWriter_1_1<'value, W>;
type EExpWriter = TextEExpWriter_1_1<'value, W>;

const IS_HUMAN_READABLE: bool = true;

// For all of the scalars, delegate to the existing 1.0 writing logic.
delegate! {
to self.value_writer_1_0 {
Expand Down Expand Up @@ -121,6 +123,7 @@ impl<'value, W: Write + 'value> ValueWriter for TextAnnotatedValueWriter_1_1<'va
type SExpWriter = TextSExpWriter_1_1<'value, W>;
type StructWriter = TextStructWriter_1_1<'value, W>;
type EExpWriter = TextEExpWriter_1_1<'value, W>;
const IS_HUMAN_READABLE: bool = true;
// For all of the scalars, delegate to the existing 1.0 writing logic.
delegate! {
to self.value_writer_1_0 {
Expand Down Expand Up @@ -227,6 +230,7 @@ impl<'value, W: Write> MakeValueWriter for TextStructWriter_1_1<'value, W> {
}

impl<'value, W: Write> StructWriter for TextStructWriter_1_1<'value, W> {
const IS_HUMAN_READABLE: bool = true;
fn close(self) -> IonResult<()> {
self.writer_1_0.close()
}
Expand Down
5 changes: 5 additions & 0 deletions src/lazy/encoder/value_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub trait ValueWriter: AnnotatableWriter + Sized {
type SExpWriter: SequenceWriter<Resources = ()>;
type StructWriter: StructWriter;
type EExpWriter: EExpWriter<Resources = ()>;
const IS_HUMAN_READABLE: bool;

fn write_null(self, ion_type: IonType) -> IonResult<()>;
fn write_bool(self, value: bool) -> IonResult<()>;
Expand Down Expand Up @@ -236,6 +237,7 @@ impl<'field, StructWriterType: StructWriter> ValueWriter for FieldWriter<'field,
<<StructWriterType as MakeValueWriter>::ValueWriter<'field> as ValueWriter>::StructWriter;
type EExpWriter =
<<StructWriterType as MakeValueWriter>::ValueWriter<'field> as ValueWriter>::EExpWriter;
const IS_HUMAN_READABLE: bool = StructWriterType::IS_HUMAN_READABLE;

delegate_value_writer_to!(fallible closure |self_: Self| {
self_.struct_writer.encode_field_name(self_.name)?;
Expand Down Expand Up @@ -287,6 +289,8 @@ impl<'field, StructWriterType: StructWriter> AnnotatableWriter
impl<'field, StructWriterType: StructWriter> ValueWriter
for AnnotatedFieldWriter<'field, StructWriterType>
{
const IS_HUMAN_READABLE: bool = StructWriterType::IS_HUMAN_READABLE;

type ListWriter =
<<<StructWriterType as MakeValueWriter>::ValueWriter<'field> as AnnotatableWriter>::AnnotatedValueWriter<'field> as ValueWriter>::ListWriter;
type SExpWriter =
Expand All @@ -304,6 +308,7 @@ impl<'field, StructWriterType: StructWriter> ValueWriter
}

pub trait StructWriter: FieldEncoder + MakeValueWriter + Sized {
const IS_HUMAN_READABLE: bool;
/// Writes a struct field using the provided name/value pair.
fn write<A: AsRawSymbolRef, V: WriteAsIon>(
&mut self,
Expand Down
2 changes: 2 additions & 0 deletions src/lazy/encoder/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl<'value, V: ValueWriter> ValueWriter for ApplicationValueWriter<'value, V> {
type SExpWriter = ApplicationSExpWriter<'value, V>;
type StructWriter = ApplicationStructWriter<'value, V>;
type EExpWriter = ApplicationEExpWriter<'value, V>;
const IS_HUMAN_READABLE: bool = V::IS_HUMAN_READABLE;

delegate! {
to self.raw_value_writer {
Expand Down Expand Up @@ -403,6 +404,7 @@ impl<'value, V: ValueWriter> FieldEncoder for ApplicationStructWriter<'value, V>
}

impl<'value, V: ValueWriter> StructWriter for ApplicationStructWriter<'value, V> {
const IS_HUMAN_READABLE: bool = V::IS_HUMAN_READABLE;
fn close(self) -> IonResult<()> {
self.raw_struct_writer.close()
}
Expand Down
2 changes: 2 additions & 0 deletions src/lazy/never.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl FieldEncoder for Never {
}

impl StructWriter for Never {
const IS_HUMAN_READABLE: bool = false;
fn close(self) -> IonResult<()> {
unreachable!("StructWriter::end in Never")
}
Expand Down Expand Up @@ -102,6 +103,7 @@ impl ValueWriter for Never {
type SExpWriter = Never;
type StructWriter = Never;
type EExpWriter = Never;
const IS_HUMAN_READABLE: bool = false;

delegate_value_writer_to_self!();
}
10 changes: 8 additions & 2 deletions src/lazy/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::lazy::text::raw::v1_1::reader::MacroAddress;
use crate::lazy::value::LazyValue;
use crate::read_config::ReadConfig;
use crate::result::IonFailure;
use crate::{IonError, IonResult};
use crate::{AnyEncoding, IonEncoding, IonError, IonResult};

/// A binary reader that only reads each value that it visits upon request (that is: lazily).
///
Expand Down Expand Up @@ -71,6 +71,12 @@ pub(crate) enum NextApplicationValue<'top, D: Decoder> {
EndOfStream,
}

impl<Input: IonInput> Reader<AnyEncoding, Input> {
pub fn detected_encoding(&self) -> IonEncoding {
self.system_reader.detected_encoding()
}
}

impl<Encoding: Decoder, Input: IonInput> Reader<Encoding, Input> {
/// Returns the next top-level value in the input stream as `Ok(Some(lazy_value))`.
/// If there are no more top-level values in the stream, returns `Ok(None)`.
Expand Down Expand Up @@ -121,7 +127,7 @@ impl<Encoding: Decoder, Input: IonInput> Reader<Encoding, Input> {
config: impl Into<ReadConfig<Encoding>>,
ion_data: Input,
) -> IonResult<Reader<Encoding, Input>> {
let system_reader = SystemReader::new(config, ion_data);
let system_reader = SystemReader::new(config, ion_data)?;
Ok(Reader { system_reader })
}
}
Expand Down
27 changes: 21 additions & 6 deletions src/lazy/streaming_raw_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,28 @@ pub struct StreamingRawReader<Encoding: Decoder, Input: IonInput> {
const DEFAULT_IO_BUFFER_SIZE: usize = 4 * 1024;

impl<Encoding: Decoder, Input: IonInput> StreamingRawReader<Encoding, Input> {
pub fn new(encoding: Encoding, input: Input) -> StreamingRawReader<Encoding, Input> {
StreamingRawReader {
pub fn new(encoding: Encoding, input: Input) -> IonResult<StreamingRawReader<Encoding, Input>> {
let mut me = StreamingRawReader {
encoding,
input: input.into_data_source().into(),
saved_state: Default::default(),
stream_position: 0,
};
me.detect_encoding()?;
Ok(me)
}

fn detect_encoding<'top>(&'top mut self) -> IonResult<()> {
if self.buffer_is_empty() {
self.pull_more_data_from_source()?;
}

let available_bytes = unsafe { &*self.input.get() }.buffer();
let reader =
<Encoding::Reader<'top> as LazyRawReader<'top, Encoding>>::new(available_bytes);
self.saved_state = reader.save_state();

Ok(())
}

/// Gets a reference to the data source and tries to fill its buffer.
Expand Down Expand Up @@ -467,7 +482,7 @@ mod tests {
let empty_context = EncodingContext::empty();
let context = empty_context.get_ref();
let ion = "";
let mut reader = StreamingRawReader::new(AnyEncoding, ion.as_bytes());
let mut reader = StreamingRawReader::new(AnyEncoding, ion.as_bytes()).unwrap();
// We expect `Ok(EndOfStream)`, not `Err(Incomplete)`.
expect_end_of_stream(reader.next(context)?)?;
Ok(())
Expand All @@ -476,7 +491,7 @@ mod tests {
fn read_example_stream(input: impl IonInput) -> IonResult<()> {
let empty_context = EncodingContext::empty();
let context = empty_context.get_ref();
let mut reader = StreamingRawReader::new(AnyEncoding, input);
let mut reader = StreamingRawReader::new(AnyEncoding, input).unwrap();
expect_string(reader.next(context)?, "foo")?;
expect_string(reader.next(context)?, "bar")?;
expect_string(reader.next(context)?, "baz")?;
Expand Down Expand Up @@ -524,7 +539,7 @@ mod tests {
fn read_invalid_example_stream(input: impl IonInput) -> IonResult<()> {
let empty_context = EncodingContext::empty();
let context = empty_context.get_ref();
let mut reader = StreamingRawReader::new(AnyEncoding, input);
let mut reader = StreamingRawReader::new(AnyEncoding, input).unwrap();
let result = reader.next(context);
// Because the input stream is exhausted, the incomplete value is illegal data and raises
// a decoding error.
Expand Down Expand Up @@ -572,7 +587,7 @@ mod tests {
// contains incomplete data that could be misinterpreted by a reader.
let empty_context = EncodingContext::empty();
let context = empty_context.get_ref();
let mut reader = StreamingRawReader::new(v1_0::Text, IonStream::new(input));
let mut reader = StreamingRawReader::new(v1_0::Text, IonStream::new(input)).unwrap();

assert_eq!(reader.next(context)?.expect_ivm()?.version(), (1, 0));
assert_eq!(
Expand Down
16 changes: 8 additions & 8 deletions src/lazy/system_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ impl<Encoding: Decoder, Input: IonInput> SystemReader<Encoding, Input> {
pub fn new(
config: impl Into<ReadConfig<Encoding>>,
input: Input,
) -> SystemReader<Encoding, Input> {
) -> IonResult<SystemReader<Encoding, Input>> {
let config = config.into();
let raw_reader = StreamingRawReader::new(config.encoding(), input);
let raw_reader = StreamingRawReader::new(config.encoding(), input)?;
let expanding_reader = ExpandingReader::new(raw_reader, config.catalog);
SystemReader { expanding_reader }
Ok(SystemReader { expanding_reader })
}

// Returns `true` if the provided [`LazyRawValue`] is a struct whose first annotation is
Expand Down Expand Up @@ -368,7 +368,7 @@ mod tests {
hello
"#,
)?;
let mut system_reader = SystemReader::new(Binary, ion_data);
let mut system_reader = SystemReader::new(Binary, ion_data)?;
loop {
match system_reader.next_item()? {
SystemStreamItem::VersionMarker(marker) => {
Expand All @@ -393,7 +393,7 @@ mod tests {
)
"#,
)?;
let mut system_reader = SystemReader::new(Binary, ion_data);
let mut system_reader = SystemReader::new(Binary, ion_data)?;
loop {
match system_reader.next_item()? {
SystemStreamItem::Value(value) => {
Expand All @@ -420,7 +420,7 @@ mod tests {
}
"#,
)?;
let mut system_reader = SystemReader::new(Binary, ion_data);
let mut system_reader = SystemReader::new(Binary, ion_data)?;
loop {
match system_reader.next_item()? {
SystemStreamItem::Value(value) => {
Expand All @@ -441,14 +441,14 @@ mod tests {
use crate::{MapCatalog, SharedSymbolTable};

fn system_reader_for<I: IonInput>(ion: I) -> SystemReader<AnyEncoding, I> {
SystemReader::new(AnyEncoding, ion)
SystemReader::new(AnyEncoding, ion).unwrap()
}

fn system_reader_with_catalog_for<Input: IonInput>(
input: Input,
catalog: impl Catalog + 'static,
) -> SystemReader<AnyEncoding, Input> {
SystemReader::new(AnyEncoding.with_catalog(catalog), input)
SystemReader::new(AnyEncoding.with_catalog(catalog), input).unwrap()
}

#[test]
Expand Down
Loading

0 comments on commit c769c7d

Please sign in to comment.