-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduces a new writer API #680
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest reading the files in this order:
write_as_ion.rs
annotate.rs
encoder/mod.rs
text/raw_text_writer.rs
and the rest in any order.
🗺️ PR tour
fn annotate<'a, A: AsRawSymbolTokenRef>( | ||
&'a self, | ||
annotations: &'a [A], | ||
) -> Annotated<'a, Self, A>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ This extension method allows values being passed into writer.write(...)
to add annotations to themselves:
writer.write(30_000.annotate("lbs_bananas"))?;
fn write_as_ion<'a, W: Write + 'a, E: LazyEncoder<W>, V: AnnotatedValueWriter<'a, W, E>>( | ||
&self, | ||
annotations_writer: V, | ||
) -> IonResult<()> { | ||
let value_writer = annotations_writer.write_annotations(self.annotations.iter())?; | ||
self.value.write_as_ion_value(value_writer) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ AnnotatedValueWriter
is a staged writer. You call a method to either write annotations or no_annotations()
and upon success it returns a ValueWriter
that can be used to write the value itself.
SymbolType: AsRawSymbolTokenRef, | ||
IterType: Iterator<Item = SymbolType> + Clone, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ An earlier version of this used much more generics; I started spelling out what each generic was (e.g. SymbolType
instead of A
). I think I kind of like it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please continue this. It gets hard to keep track sometimes even when there's only a few.
} | ||
} | ||
|
||
impl<'a, W: Write> ValueWriter<'a, W, TextEncoding_1_0> for TextValueWriter_1_0<'a, W> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ The bodies of these methods are either copy/pasted from the existing raw text writer or invoke one of its methods.
impl<'a, W: Write> Drop for TextListWriter_1_0<'a, W> { | ||
fn drop(&mut self) { | ||
// If the user didn't call `end`, the closing delimiter was not written to output. | ||
// It's too late to call it here because we can't return a Result. | ||
if !self.has_been_closed { | ||
panic!("List writer was dropped without calling `end()`."); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ This is my compromise in the absence of linear types. We can't close()
in Drop
because there's no way for the application to handle a failure there. However, that also means we cannot force the writer to close automatically. This at least makes contract violations noisy -- it's not possible to forget to to close the container and keep going.
src/lazy/encoder/mod.rs
Outdated
fn write_clob<A: AsRef<[u8]>>(self, value: A) -> IonResult<()>; | ||
fn write_blob<A: AsRef<[u8]>>(self, value: A) -> IonResult<()>; | ||
fn list_writer(self) -> IonResult<E::ListWriter<'a>>; | ||
fn sexp_writer(self) -> IonResult<E::StructWriter<'a>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
for me: change the return type to SExpWriter
.
type SExpWriter<'a> = (); | ||
type StructWriter<'a> = (); | ||
type EExpressionWriter<'a> = (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
for me: mark these as TODO
.
.write("bar".as_symbol_ref())? | ||
.write(Timestamp::with_ymd(2023, 11, 9).build()?)? | ||
.write([0xE0u8, 0x01, 0x00, 0xEA])? | ||
.write([1, 2, 3])?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ There are two ways to write a list: if you have a type that represents the list and your type implements WriteAsIon[Value]
, you can pass it to .write(...)
as is done here. For a streaming list writer that can emit heterogeneous values, you can use ListWriter
.
If WriteAsIon[Value]
is implemented, it would be implemented using ListWriter
.
} | ||
} | ||
|
||
// ===== WriteAsIonValue implementations for common types ===== |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ There are common types not covered here, like Vec<T: WriteAsIon>
and several more integer types. I'd like to punt on those.
// This type allows us to define custom behavior for `null` via trait implementations. | ||
// For an example, see the `WriteAsIonValue` trait. | ||
#[derive(Debug, PartialEq, Copy, Clone)] | ||
pub struct Null(pub IonType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ We have similar wrapper types in the Element
API for Blob(Bytes)
, Clob(Bytes)
, List(Sequence)
and SExp(Sequence)
.
src/text/raw_text_writer.rs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ Changes in this file make its inner workings crate-visible so the LazyRawTextWriter_1_0
can re-use them. Down the road, this type will be removed and I'll consolidate the logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good. I do wonder whether these traits will work for the binary writer, but that doesn't necessarily need to be solved right now.
SymbolType: AsRawSymbolTokenRef, | ||
IterType: Iterator<Item = SymbolType> + Clone, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please continue this. It gets hard to keep track sometimes even when there's only a few.
Introduces a friendlier writer API that will supplant the existing
IonWriter
. For now, it's called theLazyWriter
for symmetry with theLazyReader
. However, I plan to revisit that naming scheme once these implementations are ready to become the primary (read: only) implementations in the library.The new API uses traits instead of methods to handle writing different data types, which results less noisy code. For example, this code:
produces this output:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.