Skip to content

feat(dart): Introduce id based enum serialization#3482

Open
yash-agarwa-l wants to merge 8 commits intoapache:mainfrom
yash-agarwa-l:id-based-enum
Open

feat(dart): Introduce id based enum serialization#3482
yash-agarwa-l wants to merge 8 commits intoapache:mainfrom
yash-agarwa-l:id-based-enum

Conversation

@yash-agarwa-l
Copy link
Copy Markdown
Contributor

@yash-agarwa-l yash-agarwa-l commented Mar 15, 2026

Why?

The Dart module, only supports ordinal enum serialization, where each enum
value is serialized as its position index (0, 1, 2...), which breaks in different
cases like adding or reordering values. This adds optional id based serialization
with so that we can provide unique id to every while preserving ordinal serialization
as the default fallback.

What does this PR do?

  • Adds @ForyEnumId(int id) for enum values
  • Registers ForyEnumId in analysis type detection
  • Updates enum analysis to collect IDs and emit build-time warnings for:
    - partial @ForyEnumId usage
    - duplicate IDs
  • Falls back to ordinal serialization when annotations are incomplete or duplicated
  • Extends EnumSpec with an optional idToValue map
  • Updates generated enum specs to include the ID map when valid
  • Updates EnumSerializer to use ID-based read/write when the map is present, otherwise keep ordinal behavior
  • Adds codegen and serializer tests

Related issues

AI Contribution Checklist

AI Usage Disclosure
AI assistance was used to suggest tests and for better warnings in enum_analyzer.
dart/packages/fory/lib/src/codegen/analyze/impl/struct/enum_analyzer_impl.dart
dart/packages/fory-test/test/

  • I included a completed AI Contribution Checklist in this PR description and the required AI Usage Disclosure.
  • I can explain and defend all important changes without AI help.
  • I reviewed AI-assisted code changes line by line before submission.
  • I ran adequate human verification and recorded evidence (checks run locally or in CI, pass/fail summary, and confirmation I reviewed results).
  • I added/updated tests and specs where required.
  • I validated protocol/performance impacts with evidence when applicable.
  • I verified licensing and provenance compliance.

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?
    @ForyEnumId is a new annotation users will write in their code
    ForyEnumId class is publicly accessible
  • Does this PR introduce any binary protocol compatibility change?

Benchmark

General struct performance remained similar because those benchmarks do not exercise enum serialization paths.

@yash-agarwa-l
Copy link
Copy Markdown
Contributor Author

yash-agarwa-l commented Mar 15, 2026

Firstly, Thank you sm for the clarification about this in the discussions, it took some time to complete it.
Few Questions:

  • Shall I create an issue for this and tag it there?
  • And I did a performance test by creating a different file specific to enums, shall I add that?

@chaokunyang
Copy link
Copy Markdown
Collaborator

Could we also support this pattern?

@ ForyEnum()
enum UserRole {
  guest(0),
  member(1),
  admin(2);

  @ForyEnumId()
  final int code;

  const UserRole(this.code);
}


part '../generated/enum_id_foo.g.dart';

@foryEnum
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

use uppercase?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, we should do it as per conventions, but the existing files also follow the same pattern. If you prefer, shall I make a separate PR for that first before this one?

@yash-agarwa-l
Copy link
Copy Markdown
Contributor Author

Could we also support this pattern?

@ ForyEnum()
enum UserRole {
  guest(0),
  member(1),
  admin(2);

  @ForyEnumId()
  final int code;

  const UserRole(this.code);
}

Yes, I had this in mind initially too. I went with per-value annotation because I feel the id is a metadata for values and since Dart enums don't support private fields, so the foryId ends up publicly accessible in user's code which felt a bit annoying. But I agree the field-based approach looks much cleaner for long enums. I'd be happy to change it if you prefer.

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