Skip to content

Commit

Permalink
Adds WriteAsIonSequence and WriteAsIonFields traits
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt committed Jul 9, 2024
1 parent a9e78b4 commit 7aaebcb
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 4 deletions.
38 changes: 35 additions & 3 deletions src/lazy/encoder/value_writer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::lazy::encoder::annotation_seq::{AnnotationSeq, AnnotationsVec};
use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter};
use crate::lazy::encoder::write_as_ion::WriteAsIon;
use crate::lazy::encoder::write_as_ion::{WriteAsIon, WriteAsIonFields, WriteAsIonSequence};
use crate::lazy::text::raw::v1_1::reader::MacroIdRef;
use crate::raw_symbol_ref::AsRawSymbolRef;
use crate::{Decimal, Int, IonResult, IonType, RawSymbolRef, Timestamp};
Expand Down Expand Up @@ -32,7 +32,9 @@ pub mod internal {
}

pub trait EExpWriter: SequenceWriter {
// TODO: methods for writing tagless encodings
// TODO: methods for writing tagless encodings OR
// consider whether the tagless vs non-tagless can be deduced by the writer using the
// macro signature rather than having distinct tagless methods.
}

pub trait AnnotatableWriter {
Expand Down Expand Up @@ -325,6 +327,25 @@ pub trait StructWriter: FieldEncoder + MakeValueWriter + Sized {
Ok(self)
}

/// TODO:
/// - naming: placeholder name is `write_fields`. Is this okay?
/// - can we unify this with `write_all`?
/// - Can we prevent non-terminating recursive implementations of [WriteAsIonFields] (such as
/// this example) somehow using types and/or ownership?
/// ```
/// struct Point { x: usize, y: usize }
/// impl WriteAsIonFields for Point {

Check failure on line 337 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

cannot find trait `WriteAsIonFields` in this scope

Check failure on line 337 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

cannot find trait `WriteAsIonFields` in this scope
/// fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {

Check failure on line 338 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

cannot find trait `StructWriter` in this scope

Check failure on line 338 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

cannot find type `IonResult` in this scope

Check failure on line 338 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

cannot find trait `StructWriter` in this scope

Check failure on line 338 in src/lazy/encoder/value_writer.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

cannot find type `IonResult` in this scope
/// writer.write_fields(self)?;
/// Ok(())
/// }
/// }
/// ```
fn write_fields<V: WriteAsIonFields>(&mut self, fields: V) -> IonResult<&mut Self> {
fields.write_as_ion_fields(self)?;
Ok(self)
}

fn field_writer<'a>(&'a mut self, name: impl Into<RawSymbolRef<'a>>) -> FieldWriter<'a, Self> {
FieldWriter::new(name.into(), self)
}
Expand All @@ -347,7 +368,7 @@ macro_rules! delegate_and_return_self {
};
}

pub trait SequenceWriter: MakeValueWriter {
pub trait SequenceWriter: MakeValueWriter + Sized {
/// The type returned by the [`end`](Self::close) method.
///
/// For top-level writers, this can be any resource(s) owned by the writer that need to survive
Expand Down Expand Up @@ -379,6 +400,14 @@ pub trait SequenceWriter: MakeValueWriter {
Ok(self)
}

/// TODO:
/// - naming: currently called `write_sequence`. Would something like `write_values` be better?
/// - Can we unify this with `write_all`?
fn write_sequence<S: WriteAsIonSequence>(&mut self, values: S) -> IonResult<&mut Self> {
values.write_as_ion_sequence(self)?;
Ok(self)
}

/// Closes out the sequence being written. Delimited writers can use this opportunity to emit
/// a sentinel value, and length-prefixed writers can flush any buffered data to the output
/// buffer.
Expand Down Expand Up @@ -420,6 +449,7 @@ pub trait SequenceWriter: MakeValueWriter {
self.value_writer().eexp_writer(macro_id)
}

// TODO: Could we unify this with the WriteAsIonSequence trait somehow?
fn write_list<V: WriteAsIon, I: IntoIterator<Item = V>>(
&mut self,
values: I,
Expand All @@ -428,6 +458,7 @@ pub trait SequenceWriter: MakeValueWriter {
Ok(self)
}

// TODO: Could we unify this with the WriteAsIonSequence trait somehow?
fn write_sexp<V: WriteAsIon, I: IntoIterator<Item = V>>(
&mut self,
values: I,
Expand All @@ -436,6 +467,7 @@ pub trait SequenceWriter: MakeValueWriter {
Ok(self)
}

// TODO: Could we unify this with the WriteAsIonFields trait somehow?
fn write_struct<K: AsRawSymbolRef, V: WriteAsIon, I: IntoIterator<Item = (K, V)>>(
&mut self,
fields: I,
Expand Down
218 changes: 217 additions & 1 deletion src/lazy/encoder/write_as_ion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter, ValueWrit
use crate::lazy::encoding::Encoding;
use crate::lazy::value::LazyValue;
use crate::lazy::value_ref::ValueRef;
use crate::raw_symbol_ref::AsRawSymbolRef;
use crate::{
Blob, Clob, Decimal, Element, Int, IonResult, IonType, LazyList, LazySExp, LazyStruct, Null,
RawSymbolRef, Symbol, SymbolRef, Timestamp, Value, WriteConfig,
RawSymbolRef, Struct, Symbol, SymbolRef, Timestamp, Value, WriteConfig,
};

/// Defines how a Rust type should be serialized as Ion in terms of the methods available
Expand Down Expand Up @@ -347,3 +348,218 @@ impl<T: WriteAsIon> WriteAsIon for Option<T> {
}
}
}

/// TODO:
/// - Should this return `IonResult<()>` or `IonResult<&mut Self>`?
/// - Implementations for e.g. IntoIter, etc.
/// - types: should it be possible to implement for only certain types of sequences? E.g.
pub trait WriteAsIonSequence {
/// Maps this value to the Ion data model using the provided [`SequenceWriter`] implementation.
fn write_as_ion_sequence<S: SequenceWriter>(&self, writer: &mut S) -> IonResult<()>;
}

impl<T: WriteAsIonSequence> WriteAsIonSequence for &T {
#[inline]
fn write_as_ion_sequence<S: SequenceWriter>(&self, writer: &mut S) -> IonResult<()> {
(*self).write_as_ion_sequence(writer)
}
}

// Conflicts with impl<T: WriteAsIonSequence> WriteAsIonSequence for &T
// impl<V: WriteAsIon, I: IntoIterator<Item = V>> WriteAsIonSequence for I {
// fn write_as_ion_sequence<S: SequenceWriter>(&self, writer: &mut S) -> IonResult<()> {
// writer.write_sequence(self)?;
// Ok(())
// }
// }

macro_rules! impl_write_as_ion_sequence_for_iterable {
($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => {
impl<'a, $item $(, const $n: $n_type)?> WriteAsIonSequence for $iterable
where
$item: WriteAsIon + 'a,
{
fn write_as_ion_sequence<S: SequenceWriter>(&self, writer: &mut S) -> IonResult<()> {
for item in self.iter() {
writer.write(item)?;
}
Ok(())
}
}
};
}

impl_write_as_ion_sequence_for_iterable!(Vec<T>, T);
impl_write_as_ion_sequence_for_iterable!(Option<T>, T);
impl_write_as_ion_sequence_for_iterable!(&[T], T);
impl_write_as_ion_sequence_for_iterable!([T; N], T, const N: usize);

pub trait WriteAsIonFields {
/// Maps this value to the Ion data model using the provided [`StructWriter`] implementation.
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()>;
}

impl<'a, A, T> WriteAsIonFields for (A, T)
where
A: AsRawSymbolRef + 'a,
T: WriteAsIon + 'a,
{
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
let (name, item) = self;
writer.write(name, item)?;
Ok(())
}
}

impl<T: WriteAsIonFields> WriteAsIonFields for &T {
#[inline]
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
(*self).write_as_ion_fields(writer)
}
}

// Allows splicing a struct in when writing another struct
impl WriteAsIonFields for Struct {
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
for (k, v) in self.iter() {
writer.write(k, v)?;
}
Ok(())
}
}

macro_rules! impl_write_as_ion_fields_for_iterable {
($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => {
impl<'a, $item $(, const $n: $n_type)?> WriteAsIonFields for $iterable
where $item: WriteAsIonFields + 'a,
{
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
for item in self.iter() {
writer.write_fields(item)?;
}
Ok(())
}
}
};
}

impl_write_as_ion_fields_for_iterable!(Vec<T>, T);
impl_write_as_ion_fields_for_iterable!(Option<T>, T);
impl_write_as_ion_fields_for_iterable!(&[T], T);
impl_write_as_ion_fields_for_iterable!([T; N], T, const N: usize);

mod tests {
use super::*;
use crate::lazy::encoding::TextEncoding_1_0;

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental-ion-hash)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, all)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, all)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental-ion-hash)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental)

unused import: `crate::lazy::encoding::TextEncoding_1_0`

Check warning on line 453 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

unused import: `crate::lazy::encoding::TextEncoding_1_0`
use crate::{TextFormat, Writer};

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, default)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, default)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental-ion-hash)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, experimental-ion-hash)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, all)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu, all)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, all)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, all)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental-ion-hash)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental-ion-hash)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (macos, experimental)

unused import: `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

unused imports: `TextFormat`, `Writer`

Check warning on line 454 in src/lazy/encoder/write_as_ion.rs

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

unused import: `Writer`

struct Color(u16, u16, u16);
struct Point(usize, usize);
struct ColoredPoint(Color, Point);
struct ColoredPoint2(Option<Color>, Point);
struct LineSegment(Point, Point);

impl WriteAsIonFields for Color {
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
writer.write("r", &self.0)?;
writer.write("g", &self.1)?;
writer.write("b", &self.2)?;
Ok(())
}
}

impl WriteAsIon for Point {
fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
let mut struct_writer = writer.struct_writer()?;
struct_writer.write_fields(self)?;
struct_writer.close()
}
}

impl WriteAsIonFields for Point {
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
writer.write("x", &self.0)?;
writer.write("y", &self.1)?;
Ok(())
}
}

impl WriteAsIonSequence for Point {
fn write_as_ion_sequence<S: SequenceWriter>(&self, writer: &mut S) -> IonResult<()> {
writer.write(self.0)?;
writer.write(self.1)?;
Ok(())
}
}

impl WriteAsIon for LineSegment {
fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
let mut sexp_writer = writer.sexp_writer()?;
sexp_writer.write_symbol("line")?;
sexp_writer.write_sequence(&self.0)?;
sexp_writer.write_sequence(&self.1)?;
sexp_writer.close()
}
}

impl WriteAsIon for ColoredPoint {
fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
let mut struct_writer = writer.struct_writer()?;
struct_writer.write_fields(&self.0)?;
struct_writer.write_fields(&self.1)?;
struct_writer.close()
}
}
impl WriteAsIonFields for ColoredPoint2 {
fn write_as_ion_fields<S: StructWriter>(&self, writer: &mut S) -> IonResult<()> {
writer.write_fields(&self.0)?;
writer.write_fields(&self.1)?;
Ok(())
}
}

impl WriteAsIon for ColoredPoint2 {
fn write_as_ion<V: ValueWriter>(&self, writer: V) -> IonResult<()> {
let mut struct_writer = writer.struct_writer()?;
struct_writer.write_fields(&self.0)?;
struct_writer.write_fields(&self.1)?;
struct_writer.close()
}
}

#[test]
fn test_write_as_sequence() -> IonResult<()> {
let p0 = Point(1, 2);
let p1 = Point(4, 6);
let line = LineSegment(p0, p1);

let output = WriteConfig::<TextEncoding_1_0>::new(TextFormat::Compact)
.encode_to(line, Vec::<u8>::new())?;

println!("{}", String::from_utf8(output).unwrap());
Ok(())
}

#[test]
fn test_write_as_struct() -> IonResult<()> {
let p = Point(1, 2);
let c = Color(7, 8, 9);
let cp = ColoredPoint(c, p);
let output = WriteConfig::<TextEncoding_1_0>::new(TextFormat::Compact)
.encode_to(cp, Vec::<u8>::new())?;
println!("{}", String::from_utf8(output).unwrap());
// TODO: Add an assertion
Ok(())
}

#[test]
fn test_write_as_struct_2() -> IonResult<()> {
let p = Point(1, 2);
let cp = ColoredPoint2(None, p);
let output = WriteConfig::<TextEncoding_1_0>::new(TextFormat::Compact)
.encode_to(cp, Vec::<u8>::new())?;
println!("{}", String::from_utf8(output).unwrap());
// TODO: Add an assertion
Ok(())
}
}

0 comments on commit 7aaebcb

Please sign in to comment.