Skip to content

Conversation

@jmcarp
Copy link
Contributor

@jmcarp jmcarp commented Jan 28, 2026

The sdk contains a small number of untagged unions: union types that lack a discriminator field. Because we don't have a field that tells us which variant to use, we currently model these types as interface{}.

This patch introduces interface types with variant markers for untagged unions whose fields can be distinguished using OpenAPI format or pattern metadata. On unmarshal, check each format or pattern field against the incoming data, and hydrate into the first matching variant type.

As for previous code generation changes, review DESIGN.md first, then the generated code. The code generation code is still WIP.

The sdk contains a small number of untagged unions: union types that lack a
discriminator field. Because we don't have a field that tells us which variant
to use, we currently model these types as `interface{}`.

This patch introduces interface types with variant markers for untagged unions
whose fields can be distinguished using OpenAPI `format` or `pattern` metadata.
On unmarshal, check each `format` or `pattern` field against the incoming data,
and hydrate into the first matching variant type.
@jmcarp jmcarp force-pushed the jmcarp/untagged-unions branch from 107c4e6 to de86fcb Compare January 28, 2026 19:43
Copy link
Collaborator

@sudomateo sudomateo left a comment

Choose a reason for hiding this comment

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

This approach looks good to me! I left some comments around a few areas but overall I like the approach.


func detectIPv6Format(s string) bool {
ip := net.ParseIP(s)
return ip != nil && ip.To4() == nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh Go. The difference between this and the function above it is quite subtle. I wish there were IsV4 and IsV6 methods or something.


func (v IpNet) MarshalJSON() ([]byte, error) {
if v.Value == nil {
return []byte("null"), nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we want to explicitly write null here instead of letting Go use the struct field tags omit*?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This design makes sense to me! My only question is do we only use this untagged style when the variants are the same type? I know in Rust they aren't the same type but I mean their serialized form. That is, IpNet variants are all string and IpRange are all struct{first, last}.

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.

3 participants