Skip to content
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

Adds high-level Writer API for defining macros #938

Merged
merged 18 commits into from
Mar 31, 2025
Merged

Conversation

zslayton
Copy link
Contributor

PR tour forthcoming, please stand by until this is no longer in draft mode.

Related to #920. cc @barries

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Copy link

codecov bot commented Mar 13, 2025

Codecov Report

Attention: Patch coverage is 61.96013% with 229 lines in your changes missing coverage. Please review.

Project coverage is 78.66%. Comparing base (bbcf8cb) to head (7a2f304).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/lazy/expanded/macro_table.rs 68.92% 48 Missing and 30 partials ⚠️
src/lazy/text/raw/v1_1/reader.rs 21.68% 64 Missing and 1 partial ⚠️
src/lazy/encoder/writer.rs 67.67% 4 Missing and 28 partials ⚠️
src/lazy/expanded/compiler.rs 73.77% 5 Missing and 11 partials ⚠️
src/lazy/encoder/binary/v1_1/container_writers.rs 0.00% 8 Missing ⚠️
src/lazy/binary/raw/v1_1/struct.rs 20.00% 0 Missing and 4 partials ⚠️
src/lazy/encoder/text/v1_1/value_writer.rs 89.74% 3 Missing and 1 partial ⚠️
src/lazy/expanded/template.rs 78.94% 4 Missing ⚠️
src/lazy/never.rs 0.00% 4 Missing ⚠️
src/lazy/binary/raw/v1_1/binary_buffer.rs 0.00% 0 Missing and 3 partials ⚠️
... and 6 more

❌ Your patch status has failed because the patch coverage (61.96%) is below the target coverage (85.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #938      +/-   ##
==========================================
- Coverage   78.76%   78.66%   -0.11%     
==========================================
  Files         137      137              
  Lines       34739    35203     +464     
  Branches    34739    35203     +464     
==========================================
+ Hits        27363    27691     +328     
- Misses       5378     5454      +76     
- Partials     1998     2058      +60     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor Author

@zslayton zslayton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗺️ PR Tour 🧭

Comment on lines 1 to +2
//! This program demonstrates implementing WriteAsIon using Ion 1.1's e-expressions for a more
//! compact encoding. It uses raw-level writer APIs that end users are unlikely to leverage.
//! Ion 1.1 is not yet finalized; the encoding produced by this example and the APIs it uses
//! are very likely to change.

//! compact encoding.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 Happily, this example that ended up at the top of the diff showcases the new functionality.

use ion_rs::*;

fn main() -> IonResult<()> {
#[cfg(not(feature = "experimental"))]
panic!("This example requires the 'experimental' feature to work.");
{
eprintln!("This example requires the 'experimental' feature to work. Rebuild it with the flag `--features experimental`.");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 This was originally a panic! to make it clear that the example had failed to run. However, the boilerplate output text from the panic! obscured the message explaining what had gone wrong. Now it just prints a (more detailed) explanation to STDERR.

// IDs and Ion 1.1 refers to them as part of a macro. In both cases, however, the encoding
// context is not written out in the resulting Ion stream.
// TODO: Include the symbol/macro table definitions in the resulting output stream.
#[allow(dead_code)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 This example no longer uses the raw writer to encode symbol IDs directly. The text literals are now provided to the managed Writer as would happen in most applications.

@@ -124,6 +124,7 @@ pub(crate) mod v1_1 {

pub const ION: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(1);
pub const ENCODING: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(10);
pub const MACRO_TABLE: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(14);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 The Writer can now emit a directive to append macros to the macro table. This system symbol is used in that directive.

@@ -1311,7 +1311,7 @@ mod tests {
) -> IonResult<()> {
let mut context = EncodingContext::for_ion_version(IonVersion::v1_1);
let template_macro =
TemplateCompiler::compile_from_source(context.get_ref(), macro_source)?;
TemplateCompiler::compile_from_source(context.macro_table(), macro_source)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 Previously, the TemplateCompiler would pass around an EncodingContextRef that was used to resolve macros being invoked in the provided TDL source. However, the EncodingContextRef is specific to the Reader. (The Writer has an analogous construct that is optimized for different operations.) This PR modifies the TemplateCompiler to pass around a &MacroTable instead of an EncodingContext. Both the Reader and Writer can easily provide a &MacroTable, and that was the only field in the EncodingContextRef that was being used anyway.

@@ -92,6 +96,46 @@ impl<E: Encoding, Output: Write> Writer<E, Output> {
Ok(writer)
}

pub fn compile_macro(&mut self, source: impl IonInput) -> IonResult<Macro> {
let mut reader = Reader::new(AnyEncoding, source)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 Eventually I'd like to optimize this by either accepting bulk macro definitions, re-using the Reader, or just offering a programmatic API for constructing a macro.

Comment on lines +236 to +237
// TODO: Once expression group serialization is complete, this can be replaced by a call
// to the `add_macros` system macro.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 Binary expr group writing needs to be functional before we can do this.

@@ -216,14 +216,14 @@ impl TemplateCompiler {
/// to the template without interpretation. `(literal ...)` does not appear in the compiled
/// template as there is nothing more for it to do at expansion time.
pub fn compile_from_source(
context: EncodingContextRef<'_>,
active_macros: &MacroTable,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 All changes in this file are either:

  1. Replacing the previously-passed-around EncodingContextRef with a &MacroTable.
    OR
  2. Mechanical renaming of Macro (which is now a user-facing handle) to MacroDef (which continues to not be user-visible).

@@ -203,6 +327,33 @@ impl From<SystemMacroAddress> for MacroIdRef<'_> {
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum MacroId {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 An owned variant of MacroIdRef.

Comment on lines +276 to +279
fn resolve(self, _macro_table: &'a MacroTable) -> IonResult<ResolvedId<'a>> {
// ResolvedId is already resolved
Ok(self)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪧 This is the important bit. If you pass an unresolved identifier to a raw eexp writer, it will try to resolve it. If you pass a resolved identifier to a raw eexp writer (say, from the managed writer that already resolved it), it won't need to resolve it again.

@zslayton zslayton marked this pull request as ready for review March 13, 2025 14:18
Copy link
Contributor

@popematt popematt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only quickly scanned the PR. I'll take a closer look later. I had a few drive-by thoughts though.

LocalName(CompactString),
LocalAddress(usize),
SystemAddress(SystemMacroAddress),
// TODO: Qualified names and addresses
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about system name? (Or is that part of qualified name?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the current impl doesn't have multi-module support, System is a special case. Later, it will be supplanted by QualifiedAddress and (as you mention) QualifiedName.

let mut ion_writer = Writer::new(v1_1::Binary, BufWriter::new(ion_1_1_file.as_file()))?;

// ...then we define some macros that we intend to use to encode our log data...
let macros = LogEventMacros {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this example would be clearer without having a custom struct here. I was initially confused by this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that, but it means passing two &Macro references later on instead of one "all-the-macros" reference. I don't feel strongly either way.

@zslayton zslayton requested a review from popematt March 18, 2025 15:42
Ok(ApplicationEExpWriter::new(
self.encoding,
self.value_writer_config,
self.raw_value_writer.eexp_writer(macro_id)?,
self.raw_value_writer.eexp_writer(macro_id)?, // HERE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is HERE—leftover comment?

zslayton and others added 6 commits March 31, 2025 10:15
* Adds `lazzy-soucre-location` feature flag
* Adds `row` and `prev_newline_offset`  for `IonSlice` and `IonStream`
* Adds unit tests for `IonSlice` and `IonStream`
@zslayton zslayton merged commit 7298b5b into main Mar 31, 2025
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants