GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Detect partial enum idiom by mromaszewicz · Pull Request #2418 · oapi-codegen/oapi-codegen

Conversation

@mromaszewicz

@mromaszewicz mromaszewicz commented Jun 20, 2026

Copy link
Copy Markdown
Member

Closes: #2412

An anyOf/oneOf of a bare primitive and that same primitive constrained by an enum ("a value from this enum, or any other value of the same type") previously rendered as a JSON union -- or, for a top-level component, as nothing at all -- discarding the enum's known values.

Detect this idiom and instead resolve the schema to the bare primitive (kept as a type alias, so any value stays assignable without a conversion) while still emitting the enum's known values as untyped constants. No distinct enum type and no Valid() method are generated, since any value of the primitive type is legal.

Detection is deliberately strict: it fires only on a oneOf/anyOf with exactly two inline primitive members of the same type and format, where exactly one carries an enum. Anything else -- a $ref member, three or more members, mismatched type/format, or allOf -- falls through to the existing union/merge behavior unchanged.

Implemented in GenerateGoSchema via untypedEnumUnion(); enum-value construction is factored into buildEnumValues() and shared with the existing enum branch. EnumDefinition gains ConstType and SkipValidate so constants.tmpl can emit primitive-typed constants and skip Valid() for this case. Adds the issue-2412 regression fixture.

What's broken: this is the same idiom #1189 fixed. At the property level, current releases generate a union wrapper (As*/From*/Merge* accessors); collapsing to a plain string removes that API, which is a breaking change for existing users. It is additive only for top-level components (which generate nothing today). For that reason the behavior is not yet gated -- it likely needs an opt-in output-options flag (default off) to preserve the #1189 union representation, to be revisited. See the discussion on #2412. The issue-1189 golden file is regenerated to reflect the new output.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mromaszewicz @claude
Detect partial enum idiom
e405f07
Closes: oapi-codegen#2412

An anyOf/oneOf of a bare primitive and that same primitive constrained by
an `enum` ("a value from this enum, or any other value of the same type")
previously rendered as a JSON union -- or, for a top-level component, as
nothing at all -- discarding the enum's known values.

Detect this idiom and instead resolve the schema to the bare primitive
(kept as a type alias, so any value stays assignable without a
conversion) while still emitting the enum's known values as untyped
constants. No distinct enum type and no Valid() method are generated,
since any value of the primitive type is legal.

Detection is deliberately strict: it fires only on a oneOf/anyOf with
exactly two inline primitive members of the same type and format, where
exactly one carries an enum. Anything else -- a $ref member, three or
more members, mismatched type/format, or allOf -- falls through to the
existing union/merge behavior unchanged.

Implemented in GenerateGoSchema via untypedEnumUnion(); enum-value
construction is factored into buildEnumValues() and shared with the
existing enum branch. EnumDefinition gains ConstType and SkipValidate so
constants.tmpl can emit primitive-typed constants and skip Valid() for
this case. Adds the issue-2412 regression fixture.

What's broken: this is the same idiom oapi-codegen#1189 fixed. At the property level,
current releases generate a union wrapper (As*/From*/Merge* accessors);
collapsing to a plain string removes that API, which is a breaking change
for existing users. It is additive only for top-level components (which
generate nothing today). For that reason the behavior is not yet gated --
it likely needs an opt-in output-options flag (default off) to preserve
the oapi-codegen#1189 union representation, to be revisited. See the discussion on
oapi-codegen#2412. The issue-1189 golden file is regenerated to reflect the new
output.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@mromaszewicz

mromaszewicz commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@greptileai review

greptile-apps[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds special handling for partial-enum primitive unions in generated Go models. The main changes are:

  • Detects strict two-member oneOf/anyOf primitive-plus-enum schemas.
  • Emits primitive aliases with known enum values as constants.
  • Skips Valid() methods for these open-ended enum cases.
  • Adds issue 2412 fixtures and updates the issue 1189 golden output.

Confidence Score: 3/5

These issues should be fixed before merging.

  • Formatted string unions can generate Go code that does not compile.
  • Nullable primitive union members can be collapsed into non-nullable Go types.
  • The happy-path string enum behavior is covered, but these edge cases affect the new core detector.

pkg/codegen/schema.go

Important Files Changed

Filename Overview
pkg/codegen/schema.go Adds partial-enum union detection and shared enum-value construction; the detector misses formatted-string and nullable edge cases.
pkg/codegen/codegen.go Uses primitive constant types and skips validation for untyped enums, relying on schema detection to pass only constant-capable Go types.
pkg/codegen/templates/constants.tmpl Renders enum constants with the new constant type and conditional validation method.

Reviews (1): Last reviewed commit: "Detect partial enum idiom" | Re-trigger Greptile

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

greptile-apps[bot]
greptile-apps Bot reviewed Jun 20, 2026
View reviewed changes
Comment thread pkg/codegen/schema.go
Comment on lines +874 to +878
if !isPrimitiveOAPISchema(a.Value) || !isPrimitiveOAPISchema(b.Value) {
return nil, false
}
// Same primitive type and format.
if !slices.Equal(a.Value.Type.Slice(), b.Value.Type.Slice()) || a.Value.Format != b.Value.Format {

@greptile-apps greptile-apps Bot Jun 20, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

P1 Formatted strings break constants

This detector accepts every type: string member as a primitive, but some string formats resolve to Go types that cannot be constants. For an anyOf like format: byte plus format: byte with an enum value, this branch calls oapiSchemaToGoType and can set the untyped enum's Go type to []byte. GenerateEnums only quotes values when the Go type is exactly string, so the generated constant is typed as a slice and its value is emitted as an unquoted identifier. That generated code cannot compile. The special case should reject string formats whose resolved Go type is not constant-capable, or only enable this path when the resolved Go type stays string.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread pkg/codegen/schema.go
Comment on lines +877 to +890
// Same primitive type and format.
if !slices.Equal(a.Value.Type.Slice(), b.Value.Type.Slice()) || a.Value.Format != b.Value.Format {
return nil, false
}
// Exactly one member carries the enum constraint.
aHasEnum := len(a.Value.Enum) > 0
bHasEnum := len(b.Value.Enum) > 0
if aHasEnum == bHasEnum {
return nil, false
}
if aHasEnum {
return a, true
}
return b, true

@greptile-apps greptile-apps Bot Jun 20, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

P1 Nullable member is dropped

This comparison only checks member type, format, and enum presence, so it collapses unions where the bare primitive branch is nullable: true and the enum branch is not. That schema accepts JSON null through the nullable branch, but this path returns the schema resolved from the enum-bearing member only, so the generated type becomes a plain non-nullable primitive. A required field or top-level schema using that union can no longer represent a valid null value. The detector should require matching nullable flags and preserve them, or reject nullable members from this special case.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@greptile-apps greptile-apps[bot] greptile-apps[bot] left review comments

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Allow anyOf with an enum (to indicate "a value in this enum or a plain string") to still render the enum

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@mromaszewicz
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.