Skip to content

Commit

Permalink
Implement serde Serializer/Deserializer::is_human_readable()
Browse files Browse the repository at this point in the history
  • Loading branch information
desaikd committed Jun 26, 2024
1 parent caebdbd commit 5d0c347
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 27 deletions.
27 changes: 19 additions & 8 deletions src/lazy/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::lazy::value_ref::ValueRef;
use crate::result::IonFailure;
use crate::symbol_ref::AsSymbolRef;
use crate::{
Annotations, Element, ExpandedValueSource, IntoAnnotatedElement, IonError, IonResult, IonType,
RawSymbolRef, SymbolRef, SymbolTable, Value,
Annotations, AnyEncoding, Element, ExpandedValueSource, IntoAnnotatedElement, IonEncoding,
IonError, IonResult, IonType, RawSymbolRef, SymbolRef, SymbolTable, Value,
};

/// A value in a binary Ion stream whose header has been parsed but whose body (i.e. its data) has
Expand Down Expand Up @@ -61,6 +61,17 @@ pub struct LazyValue<'top, D: Decoder> {

pub type LazyBinaryValue<'top> = LazyValue<'top, BinaryEncoding_1_0>;

impl<'top> LazyValue<'top, AnyEncoding> {
pub(crate) fn encoding(&self) -> IonEncoding {
match self.expanded_value.source {
ExpandedValueSource::ValueLiteral(value) => value.encoding(),
// For value that is part of template invocation or constructed based on a macro invocation
// result is default set to Text_1_1
_ => IonEncoding::Text_1_1,
}
}
}

impl<'top, D: Decoder> LazyValue<'top, D> {
pub(crate) fn new(expanded_value: LazyExpandedValue<'top, D>) -> LazyValue<'top, D> {
LazyValue { expanded_value }
Expand Down Expand Up @@ -496,21 +507,21 @@ mod tests {
#[case::typed_null("null.list", IonType::List)]
#[case::annotated_typed_null("foo::null.list", IonType::List.with_annotations(["foo"]))]
#[case::boolean("false", false)]
#[case::negative_int("-1", -1)]
#[case::negative_int("-1", - 1)]
#[case::int_zero("0", 0)]
#[case::positive_int("1", 1)]
#[case::float_zero("0e0", 0f64)]
#[case::float("2.5e0", 2.5f64)]
#[case::special_float("-inf", f64::neg_infinity())]
#[case::decimal("3.14159", Decimal::new(314159, -5))]
#[case::timestamp("2023-04-29T", Timestamp::with_ymd(2023, 4, 29).build()?)]
#[case::decimal("3.14159", Decimal::new(314159, - 5))]
#[case::timestamp("2023-04-29T", Timestamp::with_ymd(2023, 4, 29).build() ?)]
#[case::symbol("foo", Symbol::owned("foo"))]
#[case::string("\"hello\"", "hello")]
#[case::blob("{{Blob}}", Element::blob([0x06, 0x5A, 0x1B]))]
#[case::clob("{{\"Clob\"}}", Element::clob("Clob".as_bytes()))]
#[case::list("[1, 2, 3]", ion_list![1, 2, 3])]
#[case::sexp("(1 2 3)", ion_sexp!(1 2 3))]
#[case::struct_("{foo: 1, bar: 2}", ion_struct! {"foo": 1, "bar": 2})]
#[case::list("[1, 2, 3]", ion_list ! [1, 2, 3])]
#[case::sexp("(1 2 3)", ion_sexp ! (1 2 3))]
#[case::struct_("{foo: 1, bar: 2}", ion_struct ! {"foo": 1, "bar": 2})]
fn try_into_element(
#[case] ion_text: &str,
#[case] expected: impl Into<Element>,
Expand Down
6 changes: 6 additions & 0 deletions src/serde/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ impl<'a, 'de> ValueDeserializer<'a, 'de> {
impl<'a, 'de> de::Deserializer<'de> for ValueDeserializer<'a, 'de> {
type Error = IonError;

/// Determine whether Deserialize implementations should expect to deserialize their human-readable form.
/// For binary Ion this will return `false` and for text Ion this will return `true`.
fn is_human_readable(&self) -> bool {
self.value.encoding().is_text()
}

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
Expand Down
20 changes: 20 additions & 0 deletions src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ pub use ser::{to_pretty, to_string};
#[cfg(feature = "experimental-serde")]
mod tests {
use crate::serde::{from_ion, to_pretty, to_string};
use std::net::IpAddr;

use crate::serde::ser::to_binary;
use crate::{Decimal, Element, Timestamp};
use chrono::{DateTime, FixedOffset, Utc};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -356,4 +358,22 @@ mod tests {
let expected = String::from("'embedded quotes'");
assert_eq!(expected, from_ion::<String, _>(i).unwrap());
}

#[test]
fn human_readable() {
// IpAddr has different repr based on if codec is considered
// human readable or not {true: string, false: byte array}
let ip: IpAddr = "127.0.0.1".parse().unwrap();
let expected_binary = [
224, 1, 0, 234, 235, 129, 131, 216, 134, 113, 3, 135, 179, 130, 86, 52, 233, 129, 138,
182, 33, 127, 32, 32, 33, 1,
];
let expected_s = "\"127.0.0.1\" ";
let binary = to_binary(&ip).unwrap();
assert_eq!(&binary[..], &expected_binary[..]);
let s = to_string(&ip).unwrap();
assert_eq!(s, expected_s);
assert_eq!(&from_ion::<IpAddr, _>(s).unwrap(), &ip);
assert_eq!(&from_ion::<IpAddr, _>(binary).unwrap(), &ip);
}
}
56 changes: 37 additions & 19 deletions src/serde/ser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::io::Write;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

Expand All @@ -13,24 +12,18 @@ use crate::result::IonFailure;
use crate::serde::decimal::TUNNELED_DECIMAL_TYPE_NAME;
use crate::serde::timestamp::TUNNELED_TIMESTAMP_TYPE_NAME;
use crate::symbol_ref::AsSymbolRef;
use crate::write_config::WriteConfig;
use crate::write_config::{WriteConfig, WriteConfigKind};
use crate::Value::Null;
use crate::{Decimal, IonError, IonResult, IonType, TextFormat, Timestamp};

pub fn write_to<T: Serialize, E: Encoding, O: Write>(
value: &T,
writer: &mut Writer<E, O>,
) -> IonResult<()> {
let serializer = ValueSerializer::new(writer.value_writer());
value.serialize(serializer)
}

fn write_with_config<T: Serialize, E: Encoding>(
value: &T,
config: WriteConfig<E>,
) -> IonResult<Vec<u8>> {
let is_human_readable = matches!(config.kind, WriteConfigKind::Text(_));
let mut writer = Writer::new(config, vec![])?;
write_to(value, &mut writer)?;
let serializer = ValueSerializer::new(writer.value_writer(), is_human_readable);
value.serialize(serializer)?;
writer.close()
}

Expand Down Expand Up @@ -74,13 +67,15 @@ where
/// Implements a standard serializer for Ion
pub struct ValueSerializer<'a, V: ValueWriter> {
pub(crate) value_writer: V,
pub(crate) is_human_readable: bool,
lifetime: PhantomData<&'a ()>,
}

impl<'a, V: ValueWriter> ValueSerializer<'a, V> {
pub fn new(value_writer: V) -> Self {
pub fn new(value_writer: V, is_human_readable: bool) -> Self {
Self {
value_writer,
is_human_readable,
lifetime: PhantomData,
}
}
Expand All @@ -98,6 +93,12 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
type SerializeStruct = MapWriter<V>;
type SerializeStructVariant = MapWriter<V::AnnotatedValueWriter<'a>>;

/// Determine whether Serialize implementations should serialize in human-readable form.
/// For binary Ion this will return `false` and for text Ion this will return `true`.
fn is_human_readable(&self) -> bool {
self.is_human_readable
}

/// Serialize a boolean to a bool value
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
self.value_writer.write(v)
Expand Down Expand Up @@ -236,18 +237,21 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
{
value.serialize(ValueSerializer::new(
self.value_writer.with_annotations([variant])?,
self.is_human_readable,
))
}

fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(SeqWriter {
seq_writer: self.value_writer.list_writer()?,
is_human_readable: self.is_human_readable,
})
}

fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(SeqWriter {
seq_writer: self.value_writer.list_writer()?,
is_human_readable: self.is_human_readable,
})
}

Expand All @@ -258,6 +262,7 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Ok(SeqWriter {
seq_writer: self.value_writer.list_writer()?,
is_human_readable: self.is_human_readable,
})
}

Expand All @@ -273,12 +278,14 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
.value_writer
.with_annotations([variant])?
.list_writer()?,
is_human_readable: self.is_human_readable,
})
}

fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(MapWriter {
map_writer: self.value_writer.struct_writer()?,
is_human_readable: self.is_human_readable,
})
}

Expand All @@ -289,6 +296,7 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(MapWriter {
map_writer: self.value_writer.struct_writer()?,
is_human_readable: self.is_human_readable,
})
}

Expand All @@ -304,12 +312,14 @@ impl<'a, V: ValueWriter + 'a> ser::Serializer for ValueSerializer<'a, V> {
.value_writer
.with_annotations([variant])?
.struct_writer()?,
is_human_readable: self.is_human_readable,
})
}
}

pub struct SeqWriter<V: ValueWriter> {
seq_writer: V::ListWriter,
is_human_readable: bool,
}

impl<V: ValueWriter> Deref for SeqWriter<V> {
Expand All @@ -334,7 +344,8 @@ impl<V: ValueWriter> ser::SerializeSeq for SeqWriter<V> {
where
T: ?Sized + Serialize,
{
value.serialize(ValueSerializer::new(self.value_writer()))
let is_human_readable = self.is_human_readable;
value.serialize(ValueSerializer::new(self.value_writer(), is_human_readable))
}

fn end(self) -> Result<Self::Ok, Self::Error> {
Expand All @@ -350,7 +361,8 @@ impl<V: ValueWriter> ser::SerializeTuple for SeqWriter<V> {
where
T: ?Sized + Serialize,
{
value.serialize(ValueSerializer::new(self.value_writer()))
let is_human_readable = self.is_human_readable;
value.serialize(ValueSerializer::new(self.value_writer(), is_human_readable))
}

fn end(self) -> Result<Self::Ok, Self::Error> {
Expand All @@ -366,7 +378,8 @@ impl<V: ValueWriter> ser::SerializeTupleStruct for SeqWriter<V> {
where
T: ?Sized + Serialize,
{
value.serialize(ValueSerializer::new(self.value_writer()))
let is_human_readable = self.is_human_readable;
value.serialize(ValueSerializer::new(self.value_writer(), is_human_readable))
}

fn end(self) -> Result<Self::Ok, Self::Error> {
Expand All @@ -382,7 +395,8 @@ impl<V: ValueWriter> ser::SerializeTupleVariant for SeqWriter<V> {
where
T: ?Sized + Serialize,
{
value.serialize(ValueSerializer::new(self.value_writer()))
let is_human_readable = self.is_human_readable;
value.serialize(ValueSerializer::new(self.value_writer(), is_human_readable))
}

fn end(self) -> Result<Self::Ok, Self::Error> {
Expand All @@ -392,6 +406,7 @@ impl<V: ValueWriter> ser::SerializeTupleVariant for SeqWriter<V> {

pub struct MapWriter<V: ValueWriter> {
map_writer: V::StructWriter,
is_human_readable: bool,
}

impl<V: ValueWriter> Deref for MapWriter<V> {
Expand Down Expand Up @@ -427,7 +442,8 @@ impl<V: ValueWriter> ser::SerializeMap for MapWriter<V> {
where
T: ?Sized + Serialize,
{
let serializer = ValueSerializer::new(self.make_value_writer());
let is_human_readable = self.is_human_readable;
let serializer = ValueSerializer::new(self.make_value_writer(), is_human_readable);
value.serialize(serializer)
}

Expand All @@ -444,7 +460,8 @@ impl<V: ValueWriter> ser::SerializeStructVariant for MapWriter<V> {
where
T: ?Sized + Serialize,
{
let serializer = ValueSerializer::new(self.field_writer(key));
let is_human_readable = self.is_human_readable;
let serializer = ValueSerializer::new(self.field_writer(key), is_human_readable);
value.serialize(serializer)
}

Expand All @@ -461,7 +478,8 @@ impl<V: ValueWriter> ser::SerializeStruct for MapWriter<V> {
where
T: ?Sized + Serialize,
{
let serializer = ValueSerializer::new(self.field_writer(key));
let is_human_readable = self.is_human_readable;
let serializer = ValueSerializer::new(self.field_writer(key), is_human_readable);
value.serialize(serializer)
}

Expand Down

0 comments on commit 5d0c347

Please sign in to comment.