Skip to content

Proposal: incremental rollout plan for constructor-form OneOf(...) #3913

@CaliLuke

Description

@CaliLuke

PR #3911 is the wrong delivery vehicle for constructor-form OneOf(...), but I still need the capability and would like to reintroduce it as a small PR stack that is easier to review.

Rather than reviving that branch, I plan to rebuild the feature from scratch on top of v3. Each PR would add one bounded behavior change, keep the repo in a valid state after merge, and isolate codegen/go_transform.go until the end.

Proposed rollout

1. Existing union/runtime hardening

Behavior change:

  • Fix specific bugs in already-supported behavior only.
  • No constructor-form OneOf(...) syntax is added yet.

Scope:

  • Only concrete fixes that stand on their own, such as missing nil/empty-value handling in existing union-related generation/runtime paths.
  • No new DSL, service, HTTP, OpenAPI, or gRPC feature surface.

Validation:

  • One regression test per fix.
  • make test must pass.

2. DSL and expression support for constructor-form OneOf(...)

Behavior change:

  • Accept OneOf(TextPayload, JSONPayload) wherever a data type is allowed.
  • Keep declaration-form OneOf("pet", func() { ... }) unchanged.
  • Support named branches, string references to named types, forward refs, and recursion where needed.
  • Still reject unsupported downstream surfaces early with precise validation errors.

New concepts:

  • Constructor-form union expression.
  • Stable internal branch identity for constructor-form unions.

Validation:

  • DSL/expr tests for dispatch, malformed usage, named/string refs, forward refs, recursion, and unsupported-placement errors.
  • make test must pass.

3. Service codegen and validation for constructor unions

Behavior change:

  • Supported constructor-form payload/result types generate concrete service-layer sum types.
  • Generated unions expose kind/constructor/AsX/SetX helpers.
  • Validation checks only the active branch.
  • Still no transport, OpenAPI, or gRPC support.

New concepts:

  • Generated service-layer constructor unions.
  • Active-branch-only validation.

Validation:

  • Service/codegen tests for top-level and nested unions, naming collisions after Go normalization, generated helpers, and kind constants.
  • Validation tests for active-branch behavior and precise rejection of unsupported placements.
  • make test must pass.

4. HTTP JSON runtime support

Behavior change:

  • HTTP client/server encode/decode supports constructor unions in JSON request/response bodies.
  • Top-level and nested body unions are supported.
  • Custom discriminator/value keys are honored in runtime bodies.
  • Multipart and non-body placements are still rejected.

New concepts:

  • Canonical HTTP JSON wire shape for constructor unions.
  • HTTP client/server symmetry for constructor-union bodies.

Validation:

  • HTTP tests for top-level/nested request and response bodies, custom keys, required vs optional body handling, and explicit rejection of multipart/params/headers/cookies.
  • make test must pass.

5. OpenAPI support, split by spec version

5A. OpenAPI v3 schemas and examples

Behavior change:

  • OpenAPI v3 emits constructor-union schemas and examples aligned with the runtime wire shape.
  • Nested, recursive, and custom-key cases are documented correctly.
  • Ambiguous raw examples are handled intentionally instead of silently selecting the wrong branch.

Validation:

  • Golden/schema/example tests for top-level, nested, recursive, custom-key, later-branch, and ambiguous-example cases.
  • make test must pass.

5B. OpenAPI v2 fallback

Behavior change:

  • Constructor unions no longer panic or emit invalid Swagger 2 output.
  • v2 degrades to a documented fallback shape rather than attempting a full discriminated-union representation.
  • Cookie-auth output remains Swagger 2 compliant.

Validation:

  • v2 generation tests for top-level, nested, recursive, and custom-key cases.
  • Checks that no unsupported v2 schema constructs leak into output.
  • make test must pass.

6. gRPC / protobuf support

Behavior change:

  • Supported constructor unions generate valid protobuf oneof shapes.
  • gRPC codegen works for supported unary constructor-union payload/result types.
  • Tag and normalized-name collisions fail before .proto generation.

New concepts:

  • Protobuf oneof representation for constructor unions.
  • Full-message namespace reservation for branch tag/name allocation.

Validation:

  • Protobuf/gRPC tests for unary payload/result unions, explicit and implicit tag collisions, normalized field-name collisions, and .proto compilation smoke tests.
  • Streaming can be deferred if that keeps the chunk smaller.
  • make test must pass.

7. Views, projections, and transform logic

Behavior change:

  • Constructor unions participate in supported view/projection paths.
  • Union-to-union transforms use stable branch identity instead of positional pairing.
  • Reordered/tagged unions transform correctly when semantically equivalent.

New concepts:

  • Branch-identity-based transform matching.
  • Final alignment between generated branch helpers, validation, and transform behavior.

Validation:

  • Transform tests for declaration-to-constructor, constructor-to-declaration, reordered branches, tagged branches, nested/collection cases, and nil branch handling.
  • View/projection tests for supported constructor-union viewed results.
  • make test must pass.

Specific feedback requested

Does this breakdown and order look reviewable?

In particular:

  1. Is service codegen before HTTP/gRPC the right first user-visible slice?
  2. Should OpenAPI v2 fallback remain separate from OpenAPI v3?
  3. Would you prefer gRPC streaming to be deferred until after unary support lands?
  4. Is isolating go_transform and view/projection work to the final chunk the right approach?

If this stack looks reasonable, I’ll restart from v3 and send the chunks in this order instead of reviving PR #3911.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions