Skip to content

refactor: attribute parsing#272

Merged
Arthurdw merged 4 commits intomainfrom
refactor/attribute-parsing
Mar 24, 2026
Merged

refactor: attribute parsing#272
Arthurdw merged 4 commits intomainfrom
refactor/attribute-parsing

Conversation

@Arthurdw
Copy link
Copy Markdown
Owner

@Arthurdw Arthurdw commented Mar 24, 2026

Resolves #127

This doesn't change any external API

Summary by CodeRabbit

  • Refactor

    • Consolidated attribute parsing logic for cleaner, more maintainable code structure
    • Simplified nullable field handling through unified field-level attributes
  • Chores

    • Removed unused code and cleaned up comments
    • Cleaned up dependency declarations

Remove TODOs for downcast-rs feature flag (#125) and serde_json dev-dep
removal (#126), both closed as won't-fix.
Replace duplicated manual Parse impls across attribute modules with a
shared declarative macro. Extract common parsing patterns into helper
functions (parse_required_string, parse_flag, parse_string_or_list).

- Merge FieldArguments and TypeSchemaArguments into a single struct
- Delete typeschema.rs (nullable absorbed into fields.rs)
- All parsers now consistently error on unknown attributes
- Net reduction of ~224 lines
@Arthurdw Arthurdw self-assigned this Mar 24, 2026
@Arthurdw Arthurdw added enhancement New feature or request release:minor labels Mar 24, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

This pull request refactors attribute parsing in ronky_derive by replacing multiple manual syn::Parse implementations with a unified define_arri_attrs! macro-based approach. The is_nullable attribute is consolidated from a separate typeschema module into the FieldArguments struct. Common parsing patterns are extracted into reusable helper functions, and the typeschema module is removed.

Changes

Cohort / File(s) Summary
Macro Infrastructure & Helper Functions
ronky_derive/src/parsers/attributes/mod.rs
Introduced define_arri_attrs! macro for generating attribute-parsing structs, added shared parsers (parse_required_string, parse_flag, parse_string_or_list) to eliminate code duplication across attribute types, and removed typeschema module declaration.
Attribute Struct Refactoring
ronky_derive/src/parsers/attributes/enum_variants.rs, fields.rs, properties.rs
Converted manual syn::Parse implementations to macro-based definitions; fields.rs gained is_nullable field; properties.rs and enum_variants.rs delegated parsing to shared helpers; removed explicit extract(attrs) functions.
Type Schema Consolidation
ronky_derive/src/parsers/attributes/typeschema.rs
Removed entire module; its is_nullable functionality is now integrated directly into FieldArguments.
Integration Update
ronky_derive/src/parsers/fields/base.rs
Refactored to derive nullable behavior from consolidated field attributes instead of separate typeschema extraction; simplified control flow while maintaining error handling for invalid nullable usage.
Dependency Cleanup
arri_repr/Cargo.toml
Removed commented-out TODO notes from dev-dependencies and dependencies sections.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Attributes once scattered, now refactored so tight,
A macro to parse them, their structure all right,
Nullable fields hop into one single place,
While helpers reusable quicken the pace!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: attribute parsing' accurately and concisely summarizes the main change—a refactoring of attribute parsing logic across multiple modules.
Linked Issues check ✅ Passed The PR directly addresses issue #127 by refactoring attribute parsing with a declarative macro approach, consolidating duplicated Parse implementations and improving consistency.
Out of Scope Changes check ✅ Passed All changes align with the refactoring objective: helper functions, macro definitions, module consolidation, and nullable handling migration are all within scope of attribute parsing improvements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/attribute-parsing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
ronky_derive/src/parsers/attributes/enum_variants.rs (1)

36-41: Error span could be improved for invalid transform values.

When EnumTransformation::try_from(s) fails, input.error(e) creates an error with the span at the current stream position (after parsing the list), rather than at the specific invalid value. This may produce less precise error locations for users.

This is a minor UX concern and acceptable for this refactor scope.

♻️ Optional: Preserve span from LitStr for better error location

To preserve spans, you could modify parse_string_or_list to return Vec<LitStr> instead of Vec<String>, then use the span from the failing literal:

"transform" => {
    let transforms = parse_string_or_list_lits(input, "transform")?;
    let transforms = transforms
        .into_iter()
        .map(|lit| {
            EnumTransformation::try_from(lit.value())
                .map_err(|e| syn::Error::new(lit.span(), e))
        })
        .collect::<Result<Vec<_>, _>>()?;
    args.transform.extend(transforms);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ronky_derive/src/parsers/attributes/enum_variants.rs` around lines 36 - 41,
The error span for invalid transform values is currently created via
input.error(e) after parsing strings, which points to the stream position
instead of the specific literal; change parsing to return literals so you can
use the literal's span when mapping to EnumTransformation: update
parse_string_or_list to a variant (e.g., parse_string_or_list_lits) that returns
Vec<LitStr>, then in the transform arm map each lit to
EnumTransformation::try_from(lit.value()).map_err(|e|
syn::Error::new(lit.span(), e)) and collect into Vec<_> before extending
args.transform, replacing use of input.error and parse_string_or_list.
ronky_derive/src/parsers/fields/base.rs (1)

41-66: Last is_nullable value wins when multiple attributes present.

The loop iterates through all field_attrs without breaking after finding the first is_nullable, meaning if a field has multiple #[arri(nullable = ...)] attributes, the last one takes precedence. This is likely fine in practice but could be surprising behavior.

Consider adding an early break after line 58 if you want first-wins semantics, or document that last-wins is intentional. Alternatively, you could error if actual_nullable is already Some to prevent conflicting attributes.

♻️ Optional: Error on duplicate nullable specification
 for attr in &field_attrs {
     if let Some(is_nullable) = attr.is_nullable {
+        if actual_nullable.is_some() {
+            return Err(quote_spanned!(field.span() =>
+                compile_error!("Multiple nullable specifications found for field")
+            )
+            .into());
+        }
         // Ensure only optional types can be nullable.
         if !is_optional && is_nullable {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ronky_derive/src/parsers/fields/base.rs` around lines 41 - 66, The code
currently lets the last #[arri(nullable = ...)] attribute win because the loop
over field_attrs keeps overwriting actual_nullable; change this to detect
duplicates and error: inside the for attr in &field_attrs loop where you check
if let Some(is_nullable) = attr.is_nullable, if actual_nullable.is_some() return
a compile_error! (use quote_spanned! with field.ty.span() to produce a helpful
message about duplicate nullable attributes), otherwise set actual_nullable =
Some(is_nullable); this prevents conflicting nullable specifications and points
the user to the field causing the issue.
ronky_derive/src/parsers/attributes/mod.rs (1)

37-41: Add validation to reject empty transform lists or clarify if intended.

parse_string_or_list accepts transform = [] and returns an empty Vec, which becomes a no-op in code generation. The function's documentation and all test cases show non-empty lists only. Either add validation to reject empty lists at parse time, or document that empty lists are intentionally allowed as no-ops.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ronky_derive/src/parsers/attributes/mod.rs` around lines 37 - 41, The parser
currently accepts an empty bracketed transform list (seen in
parse_string_or_list where you parse with Punctuated::<LitStr,
Token![,]>::parse_terminated after bracketed!(content in input)), which yields
an empty Vec and becomes a no-op; update parse_string_or_list to validate and
reject empty lists by returning a syn::Error (with a clear message like
"transform list cannot be empty") when list.is_empty(), or alternatively update
the function docs/tests to state that empty lists are permitted—preferably
implement the validation to fail at parse time so consumers get immediate
feedback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ronky_derive/src/parsers/attributes/enum_variants.rs`:
- Around line 36-41: The error span for invalid transform values is currently
created via input.error(e) after parsing strings, which points to the stream
position instead of the specific literal; change parsing to return literals so
you can use the literal's span when mapping to EnumTransformation: update
parse_string_or_list to a variant (e.g., parse_string_or_list_lits) that returns
Vec<LitStr>, then in the transform arm map each lit to
EnumTransformation::try_from(lit.value()).map_err(|e|
syn::Error::new(lit.span(), e)) and collect into Vec<_> before extending
args.transform, replacing use of input.error and parse_string_or_list.

In `@ronky_derive/src/parsers/attributes/mod.rs`:
- Around line 37-41: The parser currently accepts an empty bracketed transform
list (seen in parse_string_or_list where you parse with Punctuated::<LitStr,
Token![,]>::parse_terminated after bracketed!(content in input)), which yields
an empty Vec and becomes a no-op; update parse_string_or_list to validate and
reject empty lists by returning a syn::Error (with a clear message like
"transform list cannot be empty") when list.is_empty(), or alternatively update
the function docs/tests to state that empty lists are permitted—preferably
implement the validation to fail at parse time so consumers get immediate
feedback.

In `@ronky_derive/src/parsers/fields/base.rs`:
- Around line 41-66: The code currently lets the last #[arri(nullable = ...)]
attribute win because the loop over field_attrs keeps overwriting
actual_nullable; change this to detect duplicates and error: inside the for attr
in &field_attrs loop where you check if let Some(is_nullable) =
attr.is_nullable, if actual_nullable.is_some() return a compile_error! (use
quote_spanned! with field.ty.span() to produce a helpful message about duplicate
nullable attributes), otherwise set actual_nullable = Some(is_nullable); this
prevents conflicting nullable specifications and points the user to the field
causing the issue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c0aa378-a43a-4f00-b2bb-2bea2962ef16

📥 Commits

Reviewing files that changed from the base of the PR and between 61270db and af23519.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • arri_repr/Cargo.toml
  • ronky_derive/src/parsers/attributes/enum_variants.rs
  • ronky_derive/src/parsers/attributes/fields.rs
  • ronky_derive/src/parsers/attributes/mod.rs
  • ronky_derive/src/parsers/attributes/properties.rs
  • ronky_derive/src/parsers/attributes/typeschema.rs
  • ronky_derive/src/parsers/fields/base.rs
💤 Files with no reviewable changes (2)
  • arri_repr/Cargo.toml
  • ronky_derive/src/parsers/attributes/typeschema.rs

@Arthurdw Arthurdw merged commit 21b8ca6 into main Mar 24, 2026
8 checks passed
@Arthurdw Arthurdw deleted the refactor/attribute-parsing branch March 24, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request release:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor the way attributes are parsed

1 participant