Skip to content

Better expression ergonomics#5

Merged
bbaldino merged 9 commits into
masterfrom
parsely_write_trait_bound
Apr 25, 2025
Merged

Better expression ergonomics#5
bbaldino merged 9 commits into
masterfrom
parsely_write_trait_bound

Conversation

@bbaldino
Copy link
Copy Markdown
Owner

This is a bunch of code that all sorta bled together. Some of it could probably be split off but, here we are.

First changes were to change how StateSync is defined: the Ctx var was moved to an associated type and StateSync is now a required supertrait of ParselyWrite. The idea here was to try and eliminate the possibility of a 'forgotten' sync call.

Then I got into the idea of making expressions that are passed in attributes more ergonomic. Previously, when using something like a closer, the return type needed to be explicitly state and the result had to be a ParselyResult. This makes the annotations feel a lot more verbose. I want to be able to go from:

    #[parsely_read(map = "|v: u8| -> ParselyResult<String> { Ok(v.to_string()) }")]

to

    #[parsely_read(map = "|v: u8| { v.to_string() }")]

while also allowing

    #[parsely_write(map = "|v: &str| { v.parse::<u8>() }")]

(where v.parse::() returns a Result type) all while keeping things terse. This sent me down a whole rabbit hole that resulted in refactoring the definition of the ParselyWrite trait to shift around some generics, but so far it does appear to be working as expected.

I also cleaned up some of the codegen code. It's in better shape but lots of work still necessary there.

the current code shows a half-completed implementation of moving
generics out of ParselyWrite's type and into associated types.  Then
ParselyWrite can be used as a trait bound, which is useful when trying
to coerce types correctly when writing (e.g. the 'map' ambiguity
problem).  Doing this requires an additional type (WriteAdaptor) which
doesn't work in its current form: instead of a single impl defined in
parsely we'd need to generate impls dynamically for every type otherwise
we run into "can't-define-foreign-trait-on-foreign-type" problems when
we generate impls in a calling crate.
instead: it looks like this accomplishes enabling ParselyWrite as a
trait bound but without requiring the 'WriteAdaptor' type to still
enable a generic buffer type as opposed to a concrete one.  I still
think the 'Ctx' type might work best as an associated type and we don't
need generics there, so need to try that.
around expressions that may or may not return a ParselyResult.

there's still cleanup to do (arguably the ParselyRead trait should be tweaked
to match ParselyWrite now)
@bbaldino bbaldino requested a review from Copilot April 25, 2025 20:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request refactors the synchronization and mapping handling for Parsely traits while also cleaning up codegen and documentation. Key changes include:

  • Renaming sync-related attributes and refactoring StateSync to use an associated type.
  • Enhancing expression ergonomics by allowing mapping closures with implicit result handling.
  • Updating generated code in both read and write implementations for better type inference and consistency.

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/ui/pass/sync.rs Renamed attribute from sync_func to sync_expr and updated comment.
tests/ui/pass/map.rs Updated mapping attributes for ParselyRead/ParselyWrite and associated tests.
impl/src/parsely_write.rs Refactored StateSync and ParselyWrite traits to use associated types.
impl/src/model_types.rs Introduced MapExpr and Assertion types and updated context handling.
impl/src/code_gen/gen_write.rs Adjusted code generation for sync expressions and mapping closures.
impl/src/code_gen/gen_read.rs Removed obsolete map read helper and integrated new map expression generation.
Notes.md Updated discussion and rationale regarding sync and mapping ergonomics.

let field_name = f.ident.as_ref().expect("Field has a name");
let field_name_string = field_name.to_string();
if let Some(ref sync_func) = f.sync_func {
if let Some(ref sync_expr) = f.sync_expr {
Copy link

Copilot AI Apr 25, 2025

Choose a reason for hiding this comment

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

Ensure that sync_expr expressions consistently return a type that is convertible via into_parsely_result to avoid unexpected type coercion failures during synchronization.

Copilot uses AI. Check for mistakes.
Comment thread impl/src/model_types.rs
syn::parse2(quote! {
(#e).into_parsely_result().with_context(|| format!("{}: expression {}", #context, #idx))?
})
.unwrap()
Copy link

Copilot AI Apr 25, 2025

Choose a reason for hiding this comment

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

Consider handling potential parse errors instead of using unwrap in Context::expressions() to improve robustness in error reporting.

Suggested change
.unwrap()
.map_err(|e| darling::Error::custom(format!("Error parsing expression {}: {}", idx, e)))

Copilot uses AI. Check for mistakes.
@bbaldino bbaldino force-pushed the parsely_write_trait_bound branch 2 times, most recently from d9befc7 to a282514 Compare April 25, 2025 21:10
@bbaldino bbaldino force-pushed the parsely_write_trait_bound branch from a282514 to 5cd49b3 Compare April 25, 2025 21:13
@bbaldino bbaldino merged commit 4af6dc4 into master Apr 25, 2025
1 check passed
@bbaldino bbaldino deleted the parsely_write_trait_bound branch April 25, 2025 21:17
@github-actions github-actions Bot mentioned this pull request Apr 25, 2025
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.

2 participants