Skip to content

feat(generator): draft immutable AckType model generation - #132

Draft
leoafarias wants to merge 5 commits into
mainfrom
feat/acktype-model-classes
Draft

feat(generator): draft immutable AckType model generation#132
leoafarias wants to merge 5 commits into
mainfrom
feat/acktype-model-classes

Conversation

@leoafarias

Copy link
Copy Markdown
Member

Summary

This draft replaces the map-backed @AckType() extension-type generator with immutable Dart model classes while keeping Ack schemas responsible for validation, defaults, codecs, and boundary serialization.

The branch is intentionally open for architecture and API review before validation. I was not able to run Dart tooling in this environment.

Main pieces included

  • Switch ack_generator from direct .g.dart ownership to SharedPartBuilder plus the source_gen combining builder.
  • Add AckModelAdapter<Boundary, Runtime, Model> as the codec-safe bridge between Ack runtime values and generated classes.
  • Add an initial class emitter for:
    • immutable object classes;
    • primitive and codec-backed value classes;
    • sealed discriminated bases and final branches;
    • nested generated models;
    • typed lists and sets;
    • explicit additional properties.
  • Add a normalized schema graph foundation with stable cross-library identities and recursive resolution states.
  • Remove the legacy TypeBuilder extension-type emitter.
  • Change custom naming so @AckType(name: 'Member') generates Member exactly, without a Type suffix.
  • Add focused adapter and generator tests, migration notes, and a detailed architecture document.

Intended generated API

final class User {
  User({
    required this.id,
    required this.name,
    required this.createdAt,
  });

  final int id;
  final String name;
  final DateTime createdAt;

  factory User.parse(Object? input);
  static SchemaResult<User> safeParse(Object? input);
  factory User.fromMap(Map<String, Object?> map);
  factory User.fromJson(Map<String, dynamic> json);
  Map<String, Object?> toMap();
  Map<String, dynamic> toJson();
}

Generated models do not implement Map<String, Object?> and do not retain a backing map as their application data model.

Serialization model

JSON boundary
    -> Ack parse
validated Ack runtime value
    -> generated runtime mapper
immutable model
    -> generated runtime mapper
Ack runtime value
    -> Ack encode
JSON boundary

Nested models use Nested.$ack.fromRuntime(...) and Nested.$ack.toRuntime(...) instead of parsing an already-decoded runtime value again. This is required for DateTime, Uri, Duration, enums, and custom codecs.

json_serializable relationship

Ack generates the conventional methods itself:

factory User.fromJson(Map<String, dynamic> json);
Map<String, dynamic> toJson();

The intent is interoperability with json_serializable custom nested types. Ack does not emit @JsonSerializable, call its private generator APIs, or introduce a hidden second generation pass.

Review focus

  1. Public class API and naming migration (UserType -> User).
  2. AckModelAdapter as the boundary/runtime bridge.
  3. Optional, nullable, and defaulted field semantics.
  4. Nested-model conversion without double codec decoding.
  5. Sealed discriminated hierarchy design.
  6. Normalized graph identity and recursive dependency handling.
  7. SharedPartBuilder configuration and coexistence with other generators.
  8. Whether additional properties should remain Map<String, Object?> or gain a configurable field/type policy.

Known draft gaps

  • Existing extension-type golden and integration fixtures are not yet migrated and are expected to fail.
  • The normalized graph is present but the AST analyzer still produces the legacy intermediate model.
  • Ack.lazy and recursive schema analysis are not wired yet.
  • Default presence and one-way transform() encode capability are not propagated into generation yet.
  • Typed map values and all imported-name collision cases need completion.
  • The analyzer/source_gen dependency range still needs modernization and validation against the current json_serializable stack.
  • Clean-build json_serializable coexistence and custom nested-type fixtures are not included yet.
  • Discriminator handling for additional-properties extraction needs focused tests and may require a follow-up adjustment.
  • Emitter cleanup and analyzer-level diagnostics remain before this is mergeable.

Validation status

Not run locally in this environment.

  • dart pub get
  • dart format --output=none --set-exit-if-changed .
  • dart analyze --fatal-infos
  • dart test
  • dart run build_runner clean
  • dart run build_runner build --delete-conflicting-outputs
  • Example package builds from no generated files
  • Built-in codec round trips
  • Custom codec round trips
  • Direct, prefixed, and re-exported nested model references
  • Self-recursive and mutually recursive models
  • Discriminated branch parse and encode
  • Optional, nullable, defaulted, and additional-property behavior
  • json_serializable builder coexistence fixture
  • json_serializable custom nested-type fixture

Architecture notes

See docs/architecture/acktype-model-generation.md for the full design, migration table, known constraints, and validation checklist.

leoafarias and others added 5 commits August 20, 2026 21:53
Replaces old code generation approach with new immutable model classes:
- New model_emitter.dart for generating immutable AckType classes
- schema_model_graph_builder.dart for schema analysis
- Removes old schema_ast_analyzer, class_builder, and related models
- Updates tests to use new v2 generation approach
- Generates .ack.dart files instead of .g.dart
- Updates documentation and examples
Ack now emits both .ack.dart model parts and .g.dart JSON helpers via an internal builder that delegates structural mapping to json_serializable while keeping schema validation and codecs in the source Ack schema.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant