From da86172c518d2e3e1222ad2df0087b1e9764704a Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Fri, 9 Jan 2026 11:03:50 -0500 Subject: [PATCH 1/6] Model single-discriminator, single-value enums as interfaces. This patch updates code generation for `oneOf` types, specifically for the case when we have exactly one discriminator field and exactly one type-varying value field. In this case, we define an interface type for the `oneOf`, and a struct wrapper for each of its variant types. Variant structs implement an empty marker method to implement the interface. We also implement custom UnmarshalJSON and MarshalJSON methods for the parent type. Note: We intentionally don't change the handling of other `oneOf` types here, but may update them in the future. --- .agents/skills/fetch-spec/SKILL.md | 1 + DESIGN.md | 252 +++ internal/generate/templates/type.go.tpl | 76 +- internal/generate/test_utils/responses_output | 1 + .../test_utils/responses_output_expected | 1 + internal/generate/test_utils/types_output | 6 + .../generate/test_utils/types_output_expected | 6 + internal/generate/types.go | 217 ++- internal/generate/types_test.go | 40 +- oxide/types.go | 1624 +++++++++++------ 10 files changed, 1661 insertions(+), 563 deletions(-) create mode 100644 DESIGN.md diff --git a/.agents/skills/fetch-spec/SKILL.md b/.agents/skills/fetch-spec/SKILL.md index 584ad3d..c0fb5d0 100644 --- a/.agents/skills/fetch-spec/SKILL.md +++ b/.agents/skills/fetch-spec/SKILL.md @@ -3,6 +3,7 @@ ## Description Use this skill when you need to look up details about the Oxide API, including: + - Finding endpoint definitions, paths, or HTTP methods - Looking up schema/type definitions from the API spec - Understanding request/response formats for API operations diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..2ba3b89 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,252 @@ +# Design Notes + +## oneOf Type Generation + +### Overview + +OpenAPI `oneOf` types represent sum types, which are values that can be one of several variant +types. Go doesn't have a native sum type, so we have to decide how to represent these values in a +safe and ergonomic way. There are a few distinct patterns of `oneOf` types in the Oxide API, related +to different `serde` tagging strategies, and we handle each of them differently. + +### Tagged union + +When a `oneOf` has: + +1. Exactly one discriminator property (a field with a single enum value per variant) +2. Exactly one multi-type property (a field whose type varies across variants) + +We generate an **interface with variant wrapper types** pattern. + +**Example: `PrivateIpStack`** + +In Rust, `PrivateIpStack` is defined as: + +```rust +#[serde(tag = "type", content = "value", rename_all = "snake_case")] +pub enum PrivateIpStack { + V4(PrivateIpv4Stack), + V6(PrivateIpv6Stack), + DualStack { v4: PrivateIpv4Stack, v6: PrivateIpv6Stack }, +} +``` + +This generates the following OpenAPI spec: + +```yaml +PrivateIpStack: + oneOf: + - type: object + properties: + type: { enum: ["v4"] } + value: { $ref: "#/components/schemas/PrivateIpv4Stack" } + required: [type, value] + - type: object + properties: + type: { enum: ["v6"] } + value: { $ref: "#/components/schemas/PrivateIpv6Stack" } + required: [type, value] + - type: object + properties: + type: { enum: ["dual_stack"] } + value: + type: object + properties: + v4: { $ref: "#/components/schemas/PrivateIpv4Stack" } + v6: { $ref: "#/components/schemas/PrivateIpv6Stack" } + required: [type, value] +``` + +This has: + +- One discriminator: `type` (with values `v4`, `v6`, `dual_stack`) +- One multi-type property: `value` (which is `PrivateIpv4Stack`, `PrivateIpv6Stack`, or an inline + object depending on variant) + +So it generates: + +```go +// Interface with marker method that all variants implement +type privateIpStackVariant interface { + isPrivateIpStackVariant() +} + +// Variant wrapper types (one per discriminator value) +type PrivateIpStackV4 struct { + Value PrivateIpv4Stack `json:"value"` +} +func (PrivateIpStackV4) isPrivateIpStackVariant() {} + +type PrivateIpStackV6 struct { + Value PrivateIpv6Stack `json:"value"` +} +func (PrivateIpStackV6) isPrivateIpStackVariant() {} + +type PrivateIpStackDualStack struct { + Value PrivateIpStackValue `json:"value"` +} +func (PrivateIpStackDualStack) isPrivateIpStackVariant() {} + +// Main type with only the value field +type PrivateIpStack struct { + Value privateIpStackVariant `json:"value,omitempty"` +} + +// Type() method derives the discriminator from the concrete Value type +func (v PrivateIpStack) Type() PrivateIpStackType { + switch v.Value.(type) { + case *PrivateIpStackV4: + return PrivateIpStackTypeV4 + case *PrivateIpStackV6: + return PrivateIpStackTypeV6 + case *PrivateIpStackDualStack: + return PrivateIpStackTypeDualStack + default: + return "" + } +} +``` + +The discriminator field is not stored in the struct. We don't want users to be able to set its value +so that it doesn't match the type of the value field. Instead, we expose a public method named after +the discriminator that returns the discriminator value. + +We also implement custom `MarshalJSON` and `UnmarshalJSON` methods for the main type. To unmarshal, +we check the discriminator field in the JSON to determine which concrete type to use for +unmarshalling the value. To marshal, we call the `Type()` method to determine which discriminator to +emit. + +**Usage examples:** + +```go +// Reading a network interface's IP stack from the API +nic, _ := client.InstanceNetworkInterfaceView(ctx, params) +ipStack := nic.IpStack + +switch v := ipStack.Value.(type) { +case *oxide.PrivateIpStackV4: + fmt.Printf("IPv4 only: %s\n", v.Value.Ip) +case *oxide.PrivateIpStackV6: + fmt.Printf("IPv6 only: %s\n", v.Value.Ip) +case *oxide.PrivateIpStackDualStack: + fmt.Printf("Dual stack: %s / %s\n", v.Value.V4.Ip, v.Value.V6.Ip) +} +``` + +```go +// Creating a network interface with an IPv4-only stack +params := oxide.InstanceNetworkInterfaceCreateParams{ + Body: &oxide.InstanceNetworkInterfaceCreate{ + Name: "my-nic", + SubnetName: "my-subnet", + VpcName: "my-vpc", + IpConfig: oxide.PrivateIpStackCreate{ + Value: &oxide.PrivateIpStackCreateV4{ + Value: oxide.PrivateIpv4StackCreate{ + Ip: oxide.Ipv4Assignment{ + Type: oxide.Ipv4AssignmentTypeExplicit, + Value: "10.0.0.5", + }, + }, + }, + }, + }, +} +``` + +### Discriminator with multiple value fields + +When a `oneOf` has a discriminator field and _multiple_ value fields, we use a flat struct that +contains all properties from all variants. Properties that have different types across variants +become `any`. + +**Example: `DiskSource`** + +In Rust, `DiskSource` is defined as: + +```rust +#[serde(tag = "type", rename_all = "snake_case")] +pub enum DiskSource { + Blank { block_size: BlockSize }, + Snapshot { snapshot_id: Uuid }, + Image { image_id: Uuid }, + ImportingBlocks { block_size: BlockSize }, +} +``` + +This generates the following OpenAPI spec: + +```yaml +DiskSource: + oneOf: + - type: object + properties: + type: { enum: ["blank"] } + block_size: { $ref: "#/components/schemas/BlockSize" } + - type: object + properties: + type: { enum: ["snapshot"] } + snapshot_id: { type: string, format: uuid } + - type: object + properties: + type: { enum: ["image"] } + image_id: { type: string, format: uuid } + - type: object + properties: + type: { enum: ["importing_blocks"] } + block_size: { $ref: "#/components/schemas/BlockSize" } +``` + +This has a discriminator (`type`) but no multi-type property. Each variant has different fields +(`block_size`, `snapshot_id`, `image_id`), not different types for the same field. So we generate a +flat struct: + +```go +type DiskSource struct { + BlockSize BlockSize `json:"block_size,omitempty"` + Type DiskSourceType `json:"type,omitempty"` + SnapshotId string `json:"snapshot_id,omitempty"` + ImageId string `json:"image_id,omitempty"` +} +``` + +If any property had different types across variants, it would become `any`. + +### Untagged union + +When a `oneOf` has no object properties (i.e., variants are primitive types or references wrapped in +`allOf`), the type becomes `interface{}`. + +**Example: `IpNet`** + +In Rust, `IpNet` is defined as: + +```rust +#[serde(untagged)] +pub enum IpNet { + V4(Ipv4Net), + V6(Ipv6Net), +} +``` + +This generates the following OpenAPI spec: + +```yaml +IpNet: + oneOf: + - title: v4 + allOf: + - $ref: "#/components/schemas/Ipv4Net" + - title: v6 + allOf: + - $ref: "#/components/schemas/Ipv6Net" +``` + +```go +type IpNet interface{} +``` + +Note: we may be able to handle these types better in the future. For example, we could detect that +all variants are effectively strings and represent `IpNet` as `string`. Alternatively, we could +represent `Ipv4Net` and `Ipv6Net` as distinct types with their own validation logic, and attempt to +unmarshal into each variant type until we find a match. diff --git a/internal/generate/templates/type.go.tpl b/internal/generate/templates/type.go.tpl index 3a18b2e..f31b175 100644 --- a/internal/generate/templates/type.go.tpl +++ b/internal/generate/templates/type.go.tpl @@ -1,5 +1,10 @@ {{splitDocString .Description}} -{{- if .Fields}} +{{- if eq .Type "interface"}} +type {{.Name}} interface { + {{.OneOfMarker}}() +} + +{{else if .Fields}} type {{.Name}} {{.Type}} { {{- range .Fields}} {{- if .Description}} @@ -9,6 +14,73 @@ type {{.Name}} {{.Type}} { {{- end}} } +{{- if .OneOfMarker}} + +func ({{.Name}}) {{.OneOfMarker}}() {} +{{- end}} +{{- if .OneOfDiscriminator}} + +func (v {{.Name}}) {{.OneOfDiscriminatorMethod}}() {{.OneOfDiscriminatorType}} { + switch v.{{.OneOfValueFieldName}}.(type) { + {{- range .OneOfVariants}} + case *{{.TypeName}}: + return {{$.OneOfDiscriminatorType}}{{.DiscriminatorEnumValue}} + {{- end}} + default: + return "" + } +} + +func (v *{{.Name}}) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"{{.OneOfDiscriminator}}"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value {{.OneOfVariantType}} + switch d.Type { + {{- range .OneOfVariants}} + case "{{.DiscriminatorValue}}": + value = &{{.TypeName}}{} + {{- end}} + default: + return fmt.Errorf("unknown variant %q, expected {{range $i, $v := .OneOfVariants}}{{if $i}} or {{end}}'{{.DiscriminatorValue}}'{{end}}", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.{{.OneOfValueFieldName}} = value + return nil +} + +func (v {{.Name}}) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["{{.OneOfDiscriminator}}"] = v.{{.OneOfDiscriminatorMethod}}() + if v.{{.OneOfValueFieldName}} != nil { + valueBytes, err := json.Marshal(v.{{.OneOfValueFieldName}}) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) +} +{{- end}} + +{{else if and (eq .Type "struct") .OneOfMarker}} +type {{.Name}} struct{} + +func ({{.Name}}) {{.OneOfMarker}}() {} + {{else}} type {{.Name}} {{.Type}} -{{end -}} +{{end}} diff --git a/internal/generate/test_utils/responses_output b/internal/generate/test_utils/responses_output index 7f8560d..feb93f1 100644 --- a/internal/generate/test_utils/responses_output +++ b/internal/generate/test_utils/responses_output @@ -8,3 +8,4 @@ package oxide // ErrorResponse is the response given when error type ErrorResponse Error + diff --git a/internal/generate/test_utils/responses_output_expected b/internal/generate/test_utils/responses_output_expected index 7f8560d..feb93f1 100644 --- a/internal/generate/test_utils/responses_output_expected +++ b/internal/generate/test_utils/responses_output_expected @@ -8,3 +8,4 @@ package oxide // ErrorResponse is the response given when error type ErrorResponse Error + diff --git a/internal/generate/test_utils/types_output b/internal/generate/test_utils/types_output index 8609163..891be70 100644 --- a/internal/generate/test_utils/types_output +++ b/internal/generate/test_utils/types_output @@ -11,26 +11,31 @@ type DiskCreate struct { DiskSource DiskSource `json:"disk_source,omitempty" yaml:"disk_source,omitempty"` } + // DiskIdentifier is parameters for the [`Disk`](omicron_common::api::external::Disk) to be attached or // detached to an instance type DiskIdentifier struct { Name Name `json:"name,omitempty" yaml:"name,omitempty"` } + // DiskSourceType is the type definition for a DiskSourceType. type DiskSourceType string + // DiskSourceSnapshot is create a disk from a disk snapshot type DiskSourceSnapshot struct { SnapshotId string `json:"snapshot_id,omitempty" yaml:"snapshot_id,omitempty"` Type DiskSourceType `json:"type,omitempty" yaml:"type,omitempty"` } + // DiskSourceImage is create a disk from a project image type DiskSourceImage struct { ImageId string `json:"image_id,omitempty" yaml:"image_id,omitempty"` Type DiskSourceType `json:"type,omitempty" yaml:"type,omitempty"` } + // DiskSource is the type definition for a DiskSource. type DiskSource struct { // SnapshotId is the type definition for a SnapshotId. @@ -41,6 +46,7 @@ type DiskSource struct { ImageId string `json:"image_id,omitempty" yaml:"image_id,omitempty"` } + // DiskSourceTypeSnapshot represents the DiskSourceType `"snapshot"`. const DiskSourceTypeSnapshot DiskSourceType = "snapshot" diff --git a/internal/generate/test_utils/types_output_expected b/internal/generate/test_utils/types_output_expected index 8609163..891be70 100644 --- a/internal/generate/test_utils/types_output_expected +++ b/internal/generate/test_utils/types_output_expected @@ -11,26 +11,31 @@ type DiskCreate struct { DiskSource DiskSource `json:"disk_source,omitempty" yaml:"disk_source,omitempty"` } + // DiskIdentifier is parameters for the [`Disk`](omicron_common::api::external::Disk) to be attached or // detached to an instance type DiskIdentifier struct { Name Name `json:"name,omitempty" yaml:"name,omitempty"` } + // DiskSourceType is the type definition for a DiskSourceType. type DiskSourceType string + // DiskSourceSnapshot is create a disk from a disk snapshot type DiskSourceSnapshot struct { SnapshotId string `json:"snapshot_id,omitempty" yaml:"snapshot_id,omitempty"` Type DiskSourceType `json:"type,omitempty" yaml:"type,omitempty"` } + // DiskSourceImage is create a disk from a project image type DiskSourceImage struct { ImageId string `json:"image_id,omitempty" yaml:"image_id,omitempty"` Type DiskSourceType `json:"type,omitempty" yaml:"type,omitempty"` } + // DiskSource is the type definition for a DiskSource. type DiskSource struct { // SnapshotId is the type definition for a SnapshotId. @@ -41,6 +46,7 @@ type DiskSource struct { ImageId string `json:"image_id,omitempty" yaml:"image_id,omitempty"` } + // DiskSourceTypeSnapshot represents the DiskSourceType `"snapshot"`. const DiskSourceTypeSnapshot DiskSourceType = "snapshot" diff --git a/internal/generate/types.go b/internal/generate/types.go index 0a11c49..4752d5c 100644 --- a/internal/generate/types.go +++ b/internal/generate/types.go @@ -60,6 +60,33 @@ type TypeTemplate struct { Type string // Fields holds the information for the field Fields []TypeField + // OneOfMarker is the marker method name for interface types (e.g., "isAddressSelectorVariant") + OneOfMarker string + // OneOfMarkerType is the interface type name for variant structs (e.g., + // "addressSelectorVariant") + OneOfMarkerType string + // OneOfDiscriminator is the discriminator property name for oneOf types (e.g., "type") + OneOfDiscriminator string + // OneOfDiscriminatorMethod is the method name that returns the discriminator (e.g., "Type") + OneOfDiscriminatorMethod string + // OneOfDiscriminatorType is the type of the discriminator (e.g., "FieldValueType") + OneOfDiscriminatorType string + // OneOfValueField is the value property name for oneOf types (e.g., "value") + OneOfValueField string + // OneOfValueFieldName is the Go field name for the value field (e.g., "Value") + OneOfValueFieldName string + // OneOfVariantType is the interface type for the Value field (e.g., "addressSelectorVariant") + OneOfVariantType string + // OneOfVariants holds discriminator value -> variant type name mapping for JSON + // marshal/unmarshal + OneOfVariants []OneOfVariant +} + +// OneOfVariant maps a discriminator value to its variant type +type OneOfVariant struct { + DiscriminatorValue string + DiscriminatorEnumValue string + TypeName string } // Render renders the TypeTemplate to a Go type. @@ -746,27 +773,17 @@ func createAllOf( } func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []EnumTemplate) { - enumTpls := make([]EnumTemplate, 0) - typeTpls := make([]TypeTemplate, 0) - - // Loop over variants, creating types and enums for nested types, and gathering metadata about - // the oneOf overall. - - // Set of candidate discriminator keys. There must be exactly zero or one discriminator key. + // First pass: identify discriminator key and find properties with multiple types across + // variants. discriminatorKeys := map[string]struct{}{} - // Map of properties to sets of variant types. We use this to identify fields with multiple - // types across variants. propToVariantTypes := map[string]map[string]struct{}{} for _, variantRef := range s.OneOf { - enumField := "" for _, propName := range sortedKeys(variantRef.Value.Properties) { propRef := variantRef.Value.Properties[propName] - propField := strcase.ToCamel(propName) if len(propRef.Value.Enum) == 1 { discriminatorKeys[propName] = struct{}{} - enumField = strcase.ToCamel(propRef.Value.Enum[0].(string)) } else if len(propRef.Value.Enum) > 1 { fmt.Printf( "[WARN] TODO: oneOf for %q -> %q enum %#v\n", @@ -774,18 +791,14 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E propName, propRef.Value.Enum, ) - } else if propRef.Value.Enum == nil && len(variantRef.Value.Properties) == 1 { - enumField = propField } + if _, ok := propToVariantTypes[propName]; !ok { propToVariantTypes[propName] = map[string]struct{}{} } goType := convertToValidGoType(propName, typeName, propRef) propToVariantTypes[propName][goType] = struct{}{} } - tt, et := populateTypeTemplates(name, variantRef.Value, enumField) - typeTpls = append(typeTpls, tt...) - enumTpls = append(enumTpls, et...) } // Check invariant: there must be exactly zero or one discriminator field. @@ -807,7 +820,175 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E } } - // Build the struct type for the oneOf field, if defined. + // If we have a discriminator and a multi-type property, use interface + variants pattern. + // This gives us type safety instead of using `any`. + if len(discriminatorKeys) == 1 && len(multiTypeProps) == 1 { + var discriminatorKey, valuePropertyName string + for k := range discriminatorKeys { + discriminatorKey = k + } + for k := range multiTypeProps { + valuePropertyName = k + } + return createInterfaceOneOf(s, typeName, discriminatorKey, valuePropertyName) + } + + // Otherwise, use flat struct pattern. + return createFlatOneOf(s, name, typeName, multiTypeProps) +} + +// createInterfaceOneOf creates a oneOf type using an interface + variant wrapper types. +// This is used when variants have the same property name but different types. +func createInterfaceOneOf( + s *openapi3.Schema, + typeName, discriminatorKey, valuePropertyName string, +) ([]TypeTemplate, []EnumTemplate) { + enumTpls := make([]EnumTemplate, 0) + typeTpls := make([]TypeTemplate, 0) + + discriminatorType := typeName + strcase.ToCamel(discriminatorKey) + + // Create the variant interface + interfaceName := toLowerFirstLetter(typeName) + "Variant" + markerMethod := "is" + typeName + "Variant" + + interfaceTpl := TypeTemplate{ + Description: fmt.Sprintf("// %s is implemented by %s variants.", interfaceName, typeName), + Name: interfaceName, + Type: "interface", + OneOfMarker: markerMethod, + } + typeTpls = append(typeTpls, interfaceTpl) + + // Collect variant info for the main type's JSON methods + var variants []OneOfVariant + + // Create discriminator enum type and variant wrapper types + for _, variantRef := range s.OneOf { + // Find the discriminator value for this variant + discRef, ok := variantRef.Value.Properties[discriminatorKey] + if !ok || len(discRef.Value.Enum) != 1 { + continue + } + discValue := discRef.Value.Enum[0].(string) + + // Create enum constant for discriminator + enums, tt, et := createStringEnum( + discRef.Value, + collectEnumStringTypes, + discriminatorType, + discriminatorType, + ) + collectEnumStringTypes = enums + typeTpls = append(typeTpls, tt...) + enumTpls = append(enumTpls, et...) + + // Create variant wrapper type (e.g., AddressSelectorExplicit) + variantTypeName := typeName + strcase.ToCamel(discValue) + + // Track variant for JSON methods + variants = append(variants, OneOfVariant{ + DiscriminatorValue: discValue, + DiscriminatorEnumValue: strcase.ToCamel(discValue), + TypeName: variantTypeName, + }) + + // Find the value property in this variant + valueRef, hasValue := variantRef.Value.Properties[valuePropertyName] + + var fields []TypeField + if hasValue { + valueType := convertToValidGoType(valuePropertyName, typeName, valueRef) + + // If the value is an inline object (not a $ref), generate its type + if valueRef.Ref == "" && valueRef.Value != nil && valueRef.Value.Type != nil && + valueRef.Value.Type.Is("object") { + inlineTypeName := typeName + strcase.ToCamel(valuePropertyName) + tt, et := populateTypeTemplates(inlineTypeName, valueRef.Value, "") + typeTpls = append(typeTpls, tt...) + enumTpls = append(enumTpls, et...) + } + + isRequired := slices.Contains(variantRef.Value.Required, valuePropertyName) + fields = []TypeField{ + { + Name: strcase.ToCamel(valuePropertyName), + Type: valueType, + MarshalKey: valuePropertyName, + Schema: valueRef, + Required: isRequired, + }, + } + } + + variantTpl := TypeTemplate{ + Description: fmt.Sprintf("// %s is a variant of %s.", variantTypeName, typeName), + Name: variantTypeName, + Type: "struct", + Fields: fields, + OneOfMarker: markerMethod, + OneOfMarkerType: interfaceName, + } + typeTpls = append(typeTpls, variantTpl) + } + + // Create the main struct with only the interface-typed value field + // The discriminator is derived via a Type() method + mainFields := []TypeField{ + { + Name: strcase.ToCamel(valuePropertyName), + Type: interfaceName, + MarshalKey: valuePropertyName, + }, + } + + mainTpl := TypeTemplate{ + Description: formatTypeDescription(typeName, s), + Name: typeName, + Type: "struct", + Fields: mainFields, + OneOfDiscriminator: discriminatorKey, + OneOfDiscriminatorMethod: strcase.ToCamel(discriminatorKey), + OneOfDiscriminatorType: discriminatorType, + OneOfValueField: valuePropertyName, + OneOfValueFieldName: strcase.ToCamel(valuePropertyName), + OneOfVariantType: interfaceName, + OneOfVariants: variants, + } + typeTpls = append(typeTpls, mainTpl) + + return typeTpls, enumTpls +} + +// createFlatOneOf creates a oneOf type using a flat struct with all properties. +// Multi-type properties become `any`. +func createFlatOneOf( + s *openapi3.Schema, + name, typeName string, + multiTypeProps map[string]struct{}, +) ([]TypeTemplate, []EnumTemplate) { + enumTpls := make([]EnumTemplate, 0) + typeTpls := make([]TypeTemplate, 0) + + // Create variant types for nested enums + for _, variantRef := range s.OneOf { + enumField := "" + for _, propName := range sortedKeys(variantRef.Value.Properties) { + propRef := variantRef.Value.Properties[propName] + propField := strcase.ToCamel(propName) + + if len(propRef.Value.Enum) == 1 { + enumField = strcase.ToCamel(propRef.Value.Enum[0].(string)) + } else if propRef.Value.Enum == nil && len(variantRef.Value.Properties) == 1 { + enumField = propField + } + } + tt, et := populateTypeTemplates(name, variantRef.Value, enumField) + typeTpls = append(typeTpls, tt...) + enumTpls = append(enumTpls, et...) + } + + // Build the struct type for the oneOf field oneOfFields := []TypeField{} seenFields := map[string]struct{}{} for _, variantRef := range s.OneOf { diff --git a/internal/generate/types_test.go b/internal/generate/types_test.go index e79422a..60b6cc6 100644 --- a/internal/generate/types_test.go +++ b/internal/generate/types_test.go @@ -520,45 +520,61 @@ func Test_createOneOf(t *testing.T) { }, typeName: "IntOrString", wantTypes: []TypeTemplate{ + // Interface for variant types + { + Description: "// intOrStringVariant is implemented by IntOrString variants.", + Name: "intOrStringVariant", + Type: "interface", + OneOfMarker: "isIntOrStringVariant", + }, { Description: "// IntOrStringType is the type definition for a IntOrStringType.", Name: "IntOrStringType", Type: "string", }, { - Description: "// IntOrStringInt is the type definition for a IntOrStringInt.\n//\n// Required fields:\n// - Type\n// - Value", + Description: "// IntOrStringInt is a variant of IntOrString.", Name: "IntOrStringInt", Type: "struct", Fields: []TypeField{ - {Name: "Type", Type: "IntOrStringType", MarshalKey: "type", Required: true}, {Name: "Value", Type: "*int", MarshalKey: "value", Required: true}, }, + OneOfMarker: "isIntOrStringVariant", + OneOfMarkerType: "intOrStringVariant", }, { - Description: "// IntOrStringString is the type definition for a IntOrStringString.\n//\n// Required fields:\n// - Type\n// - Value", + Description: "// IntOrStringString is a variant of IntOrString.", Name: "IntOrStringString", Type: "struct", Fields: []TypeField{ - {Name: "Type", Type: "IntOrStringType", MarshalKey: "type", Required: true}, {Name: "Value", Type: "string", MarshalKey: "value", Required: true}, }, + OneOfMarker: "isIntOrStringVariant", + OneOfMarkerType: "intOrStringVariant", }, { Description: "// IntOrString is a value that can be an int or a string.", Name: "IntOrString", Type: "struct", Fields: []TypeField{ + {Name: "Value", Type: "intOrStringVariant", MarshalKey: "value"}, + }, + OneOfDiscriminator: "type", + OneOfDiscriminatorMethod: "Type", + OneOfDiscriminatorType: "IntOrStringType", + OneOfValueField: "value", + OneOfValueFieldName: "Value", + OneOfVariantType: "intOrStringVariant", + OneOfVariants: []OneOfVariant{ { - Name: "Type", - Type: "IntOrStringType", - MarshalKey: "type", - FallbackDescription: true, + DiscriminatorValue: "int", + DiscriminatorEnumValue: "Int", + TypeName: "IntOrStringInt", }, { - Name: "Value", - Type: "any", - MarshalKey: "value", - FallbackDescription: true, + DiscriminatorValue: "string", + DiscriminatorEnumValue: "String", + TypeName: "IntOrStringString", }, }, }, diff --git a/oxide/types.go b/oxide/types.go index 01def1d..56c90cd 100644 --- a/oxide/types.go +++ b/oxide/types.go @@ -7,6 +7,7 @@ package oxide import ( + "encoding/json" "fmt" "io" "time" @@ -1969,188 +1970,138 @@ type CurrentUser struct { SiloName Name `json:"silo_name" yaml:"silo_name"` } +// datumVariant is implemented by Datum variants. +type datumVariant interface { + isDatumVariant() +} + // DatumType is the type definition for a DatumType. type DatumType string -// DatumBool is the type definition for a DatumBool. -// -// Required fields: -// - Datum -// - Type +// DatumBool is a variant of Datum. type DatumBool struct { - Datum *bool `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *bool `json:"datum" yaml:"datum"` } -// DatumI8 is the type definition for a DatumI8. -// -// Required fields: -// - Datum -// - Type +func (DatumBool) isDatumVariant() {} + +// DatumI8 is a variant of Datum. type DatumI8 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumU8 is the type definition for a DatumU8. -// -// Required fields: -// - Datum -// - Type +func (DatumI8) isDatumVariant() {} + +// DatumU8 is a variant of Datum. type DatumU8 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumI16 is the type definition for a DatumI16. -// -// Required fields: -// - Datum -// - Type +func (DatumU8) isDatumVariant() {} + +// DatumI16 is a variant of Datum. type DatumI16 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumU16 is the type definition for a DatumU16. -// -// Required fields: -// - Datum -// - Type +func (DatumI16) isDatumVariant() {} + +// DatumU16 is a variant of Datum. type DatumU16 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumI32 is the type definition for a DatumI32. -// -// Required fields: -// - Datum -// - Type +func (DatumU16) isDatumVariant() {} + +// DatumI32 is a variant of Datum. type DatumI32 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumU32 is the type definition for a DatumU32. -// -// Required fields: -// - Datum -// - Type +func (DatumI32) isDatumVariant() {} + +// DatumU32 is a variant of Datum. type DatumU32 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumI64 is the type definition for a DatumI64. -// -// Required fields: -// - Datum -// - Type +func (DatumU32) isDatumVariant() {} + +// DatumI64 is a variant of Datum. type DatumI64 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumU64 is the type definition for a DatumU64. -// -// Required fields: -// - Datum -// - Type +func (DatumI64) isDatumVariant() {} + +// DatumU64 is a variant of Datum. type DatumU64 struct { - Datum *int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum *int `json:"datum" yaml:"datum"` } -// DatumF32 is the type definition for a DatumF32. -// -// Required fields: -// - Datum -// - Type +func (DatumU64) isDatumVariant() {} + +// DatumF32 is a variant of Datum. type DatumF32 struct { - Datum float64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum float64 `json:"datum" yaml:"datum"` } -// DatumF64 is the type definition for a DatumF64. -// -// Required fields: -// - Datum -// - Type +func (DatumF32) isDatumVariant() {} + +// DatumF64 is a variant of Datum. type DatumF64 struct { - Datum float64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum float64 `json:"datum" yaml:"datum"` } -// DatumString is the type definition for a DatumString. -// -// Required fields: -// - Datum -// - Type +func (DatumF64) isDatumVariant() {} + +// DatumString is a variant of Datum. type DatumString struct { - Datum string `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum string `json:"datum" yaml:"datum"` } -// DatumBytes is the type definition for a DatumBytes. -// -// Required fields: -// - Datum -// - Type +func (DatumString) isDatumVariant() {} + +// DatumBytes is a variant of Datum. type DatumBytes struct { - Datum []int `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` + Datum []int `json:"datum" yaml:"datum"` } -// DatumCumulativeI64 is the type definition for a DatumCumulativeI64. -// -// Required fields: -// - Datum -// - Type +func (DatumBytes) isDatumVariant() {} + +// DatumCumulativeI64 is a variant of Datum. type DatumCumulativeI64 struct { // Datum is a cumulative or counter data type. Datum Cumulativeint64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumCumulativeU64 is the type definition for a DatumCumulativeU64. -// -// Required fields: -// - Datum -// - Type +func (DatumCumulativeI64) isDatumVariant() {} + +// DatumCumulativeU64 is a variant of Datum. type DatumCumulativeU64 struct { // Datum is a cumulative or counter data type. Datum Cumulativeuint64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumCumulativeF32 is the type definition for a DatumCumulativeF32. -// -// Required fields: -// - Datum -// - Type +func (DatumCumulativeU64) isDatumVariant() {} + +// DatumCumulativeF32 is a variant of Datum. type DatumCumulativeF32 struct { // Datum is a cumulative or counter data type. Datum Cumulativefloat `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumCumulativeF64 is the type definition for a DatumCumulativeF64. -// -// Required fields: -// - Datum -// - Type +func (DatumCumulativeF32) isDatumVariant() {} + +// DatumCumulativeF64 is a variant of Datum. type DatumCumulativeF64 struct { // Datum is a cumulative or counter data type. Datum Cumulativedouble `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramI8 is the type definition for a DatumHistogramI8. -// -// Required fields: -// - Datum -// - Type +func (DatumCumulativeF64) isDatumVariant() {} + +// DatumHistogramI8 is a variant of Datum. type DatumHistogramI8 struct { // Datum is histogram metric // @@ -2160,14 +2111,11 @@ type DatumHistogramI8 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramint8 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramU8 is the type definition for a DatumHistogramU8. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramI8) isDatumVariant() {} + +// DatumHistogramU8 is a variant of Datum. type DatumHistogramU8 struct { // Datum is histogram metric // @@ -2177,14 +2125,11 @@ type DatumHistogramU8 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramuint8 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramI16 is the type definition for a DatumHistogramI16. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramU8) isDatumVariant() {} + +// DatumHistogramI16 is a variant of Datum. type DatumHistogramI16 struct { // Datum is histogram metric // @@ -2194,14 +2139,11 @@ type DatumHistogramI16 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramint16 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramU16 is the type definition for a DatumHistogramU16. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramI16) isDatumVariant() {} + +// DatumHistogramU16 is a variant of Datum. type DatumHistogramU16 struct { // Datum is histogram metric // @@ -2211,14 +2153,11 @@ type DatumHistogramU16 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramuint16 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramI32 is the type definition for a DatumHistogramI32. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramU16) isDatumVariant() {} + +// DatumHistogramI32 is a variant of Datum. type DatumHistogramI32 struct { // Datum is histogram metric // @@ -2228,14 +2167,11 @@ type DatumHistogramI32 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramint32 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramU32 is the type definition for a DatumHistogramU32. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramI32) isDatumVariant() {} + +// DatumHistogramU32 is a variant of Datum. type DatumHistogramU32 struct { // Datum is histogram metric // @@ -2245,14 +2181,11 @@ type DatumHistogramU32 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramuint32 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramI64 is the type definition for a DatumHistogramI64. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramU32) isDatumVariant() {} + +// DatumHistogramI64 is a variant of Datum. type DatumHistogramI64 struct { // Datum is histogram metric // @@ -2262,14 +2195,11 @@ type DatumHistogramI64 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramint64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramU64 is the type definition for a DatumHistogramU64. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramI64) isDatumVariant() {} + +// DatumHistogramU64 is a variant of Datum. type DatumHistogramU64 struct { // Datum is histogram metric // @@ -2279,14 +2209,11 @@ type DatumHistogramU64 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramuint64 `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramF32 is the type definition for a DatumHistogramF32. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramU64) isDatumVariant() {} + +// DatumHistogramF32 is a variant of Datum. type DatumHistogramF32 struct { // Datum is histogram metric // @@ -2296,14 +2223,11 @@ type DatumHistogramF32 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramfloat `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumHistogramF64 is the type definition for a DatumHistogramF64. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramF32) isDatumVariant() {} + +// DatumHistogramF64 is a variant of Datum. type DatumHistogramF64 struct { // Datum is histogram metric // @@ -2313,25 +2237,179 @@ type DatumHistogramF64 struct { // // Note that any gaps, unsorted bins, or non-finite values will result in an error. Datum Histogramdouble `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } -// DatumMissing is the type definition for a DatumMissing. -// -// Required fields: -// - Datum -// - Type +func (DatumHistogramF64) isDatumVariant() {} + +// DatumMissing is a variant of Datum. type DatumMissing struct { Datum MissingDatum `json:"datum" yaml:"datum"` - Type DatumType `json:"type" yaml:"type"` } +func (DatumMissing) isDatumVariant() {} + // Datum is a `Datum` is a single sampled data point from a metric. type Datum struct { - // Datum is the type definition for a Datum. - Datum any `json:"datum,omitempty" yaml:"datum,omitempty"` - // Type is the type definition for a Type. - Type DatumType `json:"type,omitempty" yaml:"type,omitempty"` + Datum datumVariant `json:"datum,omitempty" yaml:"datum,omitempty"` +} + +func (v Datum) Type() DatumType { + switch v.Datum.(type) { + case *DatumBool: + return DatumTypeBool + case *DatumI8: + return DatumTypeI8 + case *DatumU8: + return DatumTypeU8 + case *DatumI16: + return DatumTypeI16 + case *DatumU16: + return DatumTypeU16 + case *DatumI32: + return DatumTypeI32 + case *DatumU32: + return DatumTypeU32 + case *DatumI64: + return DatumTypeI64 + case *DatumU64: + return DatumTypeU64 + case *DatumF32: + return DatumTypeF32 + case *DatumF64: + return DatumTypeF64 + case *DatumString: + return DatumTypeString + case *DatumBytes: + return DatumTypeBytes + case *DatumCumulativeI64: + return DatumTypeCumulativeI64 + case *DatumCumulativeU64: + return DatumTypeCumulativeU64 + case *DatumCumulativeF32: + return DatumTypeCumulativeF32 + case *DatumCumulativeF64: + return DatumTypeCumulativeF64 + case *DatumHistogramI8: + return DatumTypeHistogramI8 + case *DatumHistogramU8: + return DatumTypeHistogramU8 + case *DatumHistogramI16: + return DatumTypeHistogramI16 + case *DatumHistogramU16: + return DatumTypeHistogramU16 + case *DatumHistogramI32: + return DatumTypeHistogramI32 + case *DatumHistogramU32: + return DatumTypeHistogramU32 + case *DatumHistogramI64: + return DatumTypeHistogramI64 + case *DatumHistogramU64: + return DatumTypeHistogramU64 + case *DatumHistogramF32: + return DatumTypeHistogramF32 + case *DatumHistogramF64: + return DatumTypeHistogramF64 + case *DatumMissing: + return DatumTypeMissing + default: + return "" + } +} + +func (v *Datum) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value datumVariant + switch d.Type { + case "bool": + value = &DatumBool{} + case "i8": + value = &DatumI8{} + case "u8": + value = &DatumU8{} + case "i16": + value = &DatumI16{} + case "u16": + value = &DatumU16{} + case "i32": + value = &DatumI32{} + case "u32": + value = &DatumU32{} + case "i64": + value = &DatumI64{} + case "u64": + value = &DatumU64{} + case "f32": + value = &DatumF32{} + case "f64": + value = &DatumF64{} + case "string": + value = &DatumString{} + case "bytes": + value = &DatumBytes{} + case "cumulative_i64": + value = &DatumCumulativeI64{} + case "cumulative_u64": + value = &DatumCumulativeU64{} + case "cumulative_f32": + value = &DatumCumulativeF32{} + case "cumulative_f64": + value = &DatumCumulativeF64{} + case "histogram_i8": + value = &DatumHistogramI8{} + case "histogram_u8": + value = &DatumHistogramU8{} + case "histogram_i16": + value = &DatumHistogramI16{} + case "histogram_u16": + value = &DatumHistogramU16{} + case "histogram_i32": + value = &DatumHistogramI32{} + case "histogram_u32": + value = &DatumHistogramU32{} + case "histogram_i64": + value = &DatumHistogramI64{} + case "histogram_u64": + value = &DatumHistogramU64{} + case "histogram_f32": + value = &DatumHistogramF32{} + case "histogram_f64": + value = &DatumHistogramF64{} + case "missing": + value = &DatumMissing{} + default: + return fmt.Errorf("unknown variant %q, expected 'bool' or 'i8' or 'u8' or 'i16' or 'u16' or 'i32' or 'u32' or 'i64' or 'u64' or 'f32' or 'f64' or 'string' or 'bytes' or 'cumulative_i64' or 'cumulative_u64' or 'cumulative_f32' or 'cumulative_f64' or 'histogram_i8' or 'histogram_u8' or 'histogram_i16' or 'histogram_u16' or 'histogram_i32' or 'histogram_u32' or 'histogram_i64' or 'histogram_u64' or 'histogram_f32' or 'histogram_f64' or 'missing'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Datum = value + return nil +} + +func (v Datum) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Datum != nil { + valueBytes, err := json.Marshal(v.Datum) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // DerEncodedKeyPair is the type definition for a DerEncodedKeyPair. @@ -2948,135 +3026,196 @@ type FieldSource string // FieldType is the `FieldType` identifies the data type of a target or metric field. type FieldType string +// fieldValueVariant is implemented by FieldValue variants. +type fieldValueVariant interface { + isFieldValueVariant() +} + // FieldValueType is the type definition for a FieldValueType. type FieldValueType string -// FieldValueString is the type definition for a FieldValueString. -// -// Required fields: -// - Type -// - Value +// FieldValueString is a variant of FieldValue. type FieldValueString struct { - Type FieldValueType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// FieldValueI8 is the type definition for a FieldValueI8. -// -// Required fields: -// - Type -// - Value +func (FieldValueString) isFieldValueVariant() {} + +// FieldValueI8 is a variant of FieldValue. type FieldValueI8 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueU8 is the type definition for a FieldValueU8. -// -// Required fields: -// - Type -// - Value +func (FieldValueI8) isFieldValueVariant() {} + +// FieldValueU8 is a variant of FieldValue. type FieldValueU8 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueI16 is the type definition for a FieldValueI16. -// -// Required fields: -// - Type -// - Value +func (FieldValueU8) isFieldValueVariant() {} + +// FieldValueI16 is a variant of FieldValue. type FieldValueI16 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueU16 is the type definition for a FieldValueU16. -// -// Required fields: -// - Type -// - Value +func (FieldValueI16) isFieldValueVariant() {} + +// FieldValueU16 is a variant of FieldValue. type FieldValueU16 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueI32 is the type definition for a FieldValueI32. -// -// Required fields: -// - Type -// - Value +func (FieldValueU16) isFieldValueVariant() {} + +// FieldValueI32 is a variant of FieldValue. type FieldValueI32 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueU32 is the type definition for a FieldValueU32. -// -// Required fields: -// - Type -// - Value +func (FieldValueI32) isFieldValueVariant() {} + +// FieldValueU32 is a variant of FieldValue. type FieldValueU32 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueI64 is the type definition for a FieldValueI64. -// -// Required fields: -// - Type -// - Value +func (FieldValueU32) isFieldValueVariant() {} + +// FieldValueI64 is a variant of FieldValue. type FieldValueI64 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueU64 is the type definition for a FieldValueU64. -// -// Required fields: -// - Type -// - Value +func (FieldValueI64) isFieldValueVariant() {} + +// FieldValueU64 is a variant of FieldValue. type FieldValueU64 struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *int `json:"value" yaml:"value"` + Value *int `json:"value" yaml:"value"` } -// FieldValueIpAddr is the type definition for a FieldValueIpAddr. -// -// Required fields: -// - Type -// - Value +func (FieldValueU64) isFieldValueVariant() {} + +// FieldValueIpAddr is a variant of FieldValue. type FieldValueIpAddr struct { - Type FieldValueType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// FieldValueUuid is the type definition for a FieldValueUuid. -// -// Required fields: -// - Type -// - Value +func (FieldValueIpAddr) isFieldValueVariant() {} + +// FieldValueUuid is a variant of FieldValue. type FieldValueUuid struct { - Type FieldValueType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// FieldValueBool is the type definition for a FieldValueBool. -// -// Required fields: -// - Type -// - Value +func (FieldValueUuid) isFieldValueVariant() {} + +// FieldValueBool is a variant of FieldValue. type FieldValueBool struct { - Type FieldValueType `json:"type" yaml:"type"` - Value *bool `json:"value" yaml:"value"` + Value *bool `json:"value" yaml:"value"` } +func (FieldValueBool) isFieldValueVariant() {} + // FieldValue is the `FieldValue` contains the value of a target or metric field. type FieldValue struct { - // Type is the type definition for a Type. - Type FieldValueType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is the type definition for a Value. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value fieldValueVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v FieldValue) Type() FieldValueType { + switch v.Value.(type) { + case *FieldValueString: + return FieldValueTypeString + case *FieldValueI8: + return FieldValueTypeI8 + case *FieldValueU8: + return FieldValueTypeU8 + case *FieldValueI16: + return FieldValueTypeI16 + case *FieldValueU16: + return FieldValueTypeU16 + case *FieldValueI32: + return FieldValueTypeI32 + case *FieldValueU32: + return FieldValueTypeU32 + case *FieldValueI64: + return FieldValueTypeI64 + case *FieldValueU64: + return FieldValueTypeU64 + case *FieldValueIpAddr: + return FieldValueTypeIpAddr + case *FieldValueUuid: + return FieldValueTypeUuid + case *FieldValueBool: + return FieldValueTypeBool + default: + return "" + } +} + +func (v *FieldValue) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value fieldValueVariant + switch d.Type { + case "string": + value = &FieldValueString{} + case "i8": + value = &FieldValueI8{} + case "u8": + value = &FieldValueU8{} + case "i16": + value = &FieldValueI16{} + case "u16": + value = &FieldValueU16{} + case "i32": + value = &FieldValueI32{} + case "u32": + value = &FieldValueU32{} + case "i64": + value = &FieldValueI64{} + case "u64": + value = &FieldValueU64{} + case "ip_addr": + value = &FieldValueIpAddr{} + case "uuid": + value = &FieldValueUuid{} + case "bool": + value = &FieldValueBool{} + default: + return fmt.Errorf("unknown variant %q, expected 'string' or 'i8' or 'u8' or 'i16' or 'u16' or 'i32' or 'u32' or 'i64' or 'u64' or 'ip_addr' or 'uuid' or 'bool'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v FieldValue) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // FinalizeDisk is parameters for finalizing a disk @@ -5447,31 +5586,30 @@ type PoolSelector struct { IpVersion IpVersion `json:"ip_version,omitzero" yaml:"ip_version,omitzero"` } +// privateIpConfigVariant is implemented by PrivateIpConfig variants. +type privateIpConfigVariant interface { + isPrivateIpConfigVariant() +} + // PrivateIpConfigType is the type definition for a PrivateIpConfigType. type PrivateIpConfigType string -// PrivateIpConfigV4 is the interface has only an IPv4 configuration. -// -// Required fields: -// - Type -// - Value +// PrivateIpConfigV4 is a variant of PrivateIpConfig. type PrivateIpConfigV4 struct { - Type PrivateIpConfigType `json:"type" yaml:"type"` // Value is vPC-private IPv4 configuration for a network interface. Value PrivateIpv4Config `json:"value" yaml:"value"` } -// PrivateIpConfigV6 is the interface has only an IPv6 configuration. -// -// Required fields: -// - Type -// - Value +func (PrivateIpConfigV4) isPrivateIpConfigVariant() {} + +// PrivateIpConfigV6 is a variant of PrivateIpConfig. type PrivateIpConfigV6 struct { - Type PrivateIpConfigType `json:"type" yaml:"type"` // Value is vPC-private IPv6 configuration for a network interface. Value PrivateIpv6Config `json:"value" yaml:"value"` } +func (PrivateIpConfigV6) isPrivateIpConfigVariant() {} + // PrivateIpConfigValue is the type definition for a PrivateIpConfigValue. // // Required fields: @@ -5484,49 +5622,101 @@ type PrivateIpConfigValue struct { V6 PrivateIpv6Config `json:"v6" yaml:"v6"` } -// PrivateIpConfigDualStack is the interface is dual-stack. -// -// Required fields: -// - Type -// - Value +// PrivateIpConfigDualStack is a variant of PrivateIpConfig. type PrivateIpConfigDualStack struct { - Type PrivateIpConfigType `json:"type" yaml:"type"` Value PrivateIpConfigValue `json:"value" yaml:"value"` } +func (PrivateIpConfigDualStack) isPrivateIpConfigVariant() {} + // PrivateIpConfig is vPC-private IP address configuration for a network interface. type PrivateIpConfig struct { - // Type is the type definition for a Type. - Type PrivateIpConfigType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is vPC-private IPv4 configuration for a network interface. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value privateIpConfigVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v PrivateIpConfig) Type() PrivateIpConfigType { + switch v.Value.(type) { + case *PrivateIpConfigV4: + return PrivateIpConfigTypeV4 + case *PrivateIpConfigV6: + return PrivateIpConfigTypeV6 + case *PrivateIpConfigDualStack: + return PrivateIpConfigTypeDualStack + default: + return "" + } +} + +func (v *PrivateIpConfig) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value privateIpConfigVariant + switch d.Type { + case "v4": + value = &PrivateIpConfigV4{} + case "v6": + value = &PrivateIpConfigV6{} + case "dual_stack": + value = &PrivateIpConfigDualStack{} + default: + return fmt.Errorf("unknown variant %q, expected 'v4' or 'v6' or 'dual_stack'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v PrivateIpConfig) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) +} + +// privateIpStackVariant is implemented by PrivateIpStack variants. +type privateIpStackVariant interface { + isPrivateIpStackVariant() } // PrivateIpStackType is the type definition for a PrivateIpStackType. type PrivateIpStackType string -// PrivateIpStackV4 is the interface has only an IPv4 stack. -// -// Required fields: -// - Type -// - Value +// PrivateIpStackV4 is a variant of PrivateIpStack. type PrivateIpStackV4 struct { - Type PrivateIpStackType `json:"type" yaml:"type"` // Value is the VPC-private IPv4 stack for a network interface Value PrivateIpv4Stack `json:"value" yaml:"value"` } -// PrivateIpStackV6 is the interface has only an IPv6 stack. -// -// Required fields: -// - Type -// - Value +func (PrivateIpStackV4) isPrivateIpStackVariant() {} + +// PrivateIpStackV6 is a variant of PrivateIpStack. type PrivateIpStackV6 struct { - Type PrivateIpStackType `json:"type" yaml:"type"` // Value is the VPC-private IPv6 stack for a network interface Value PrivateIpv6Stack `json:"value" yaml:"value"` } +func (PrivateIpStackV6) isPrivateIpStackVariant() {} + // PrivateIpStackValue is the type definition for a PrivateIpStackValue. // // Required fields: @@ -5539,49 +5729,101 @@ type PrivateIpStackValue struct { V6 PrivateIpv6Stack `json:"v6" yaml:"v6"` } -// PrivateIpStackDualStack is the interface is dual-stack IPv4 and IPv6. -// -// Required fields: -// - Type -// - Value +// PrivateIpStackDualStack is a variant of PrivateIpStack. type PrivateIpStackDualStack struct { - Type PrivateIpStackType `json:"type" yaml:"type"` Value PrivateIpStackValue `json:"value" yaml:"value"` } +func (PrivateIpStackDualStack) isPrivateIpStackVariant() {} + // PrivateIpStack is the VPC-private IP stack for a network interface. type PrivateIpStack struct { - // Type is the type definition for a Type. - Type PrivateIpStackType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is the VPC-private IPv4 stack for a network interface - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value privateIpStackVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v PrivateIpStack) Type() PrivateIpStackType { + switch v.Value.(type) { + case *PrivateIpStackV4: + return PrivateIpStackTypeV4 + case *PrivateIpStackV6: + return PrivateIpStackTypeV6 + case *PrivateIpStackDualStack: + return PrivateIpStackTypeDualStack + default: + return "" + } +} + +func (v *PrivateIpStack) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value privateIpStackVariant + switch d.Type { + case "v4": + value = &PrivateIpStackV4{} + case "v6": + value = &PrivateIpStackV6{} + case "dual_stack": + value = &PrivateIpStackDualStack{} + default: + return fmt.Errorf("unknown variant %q, expected 'v4' or 'v6' or 'dual_stack'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v PrivateIpStack) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) +} + +// privateIpStackCreateVariant is implemented by PrivateIpStackCreate variants. +type privateIpStackCreateVariant interface { + isPrivateIpStackCreateVariant() } // PrivateIpStackCreateType is the type definition for a PrivateIpStackCreateType. type PrivateIpStackCreateType string -// PrivateIpStackCreateV4 is the interface has only an IPv4 stack. -// -// Required fields: -// - Type -// - Value +// PrivateIpStackCreateV4 is a variant of PrivateIpStackCreate. type PrivateIpStackCreateV4 struct { - Type PrivateIpStackCreateType `json:"type" yaml:"type"` // Value is configuration for a network interface's IPv4 addressing. Value PrivateIpv4StackCreate `json:"value" yaml:"value"` } -// PrivateIpStackCreateV6 is the interface has only an IPv6 stack. -// -// Required fields: -// - Type -// - Value +func (PrivateIpStackCreateV4) isPrivateIpStackCreateVariant() {} + +// PrivateIpStackCreateV6 is a variant of PrivateIpStackCreate. type PrivateIpStackCreateV6 struct { - Type PrivateIpStackCreateType `json:"type" yaml:"type"` // Value is configuration for a network interface's IPv6 addressing. Value PrivateIpv6StackCreate `json:"value" yaml:"value"` } +func (PrivateIpStackCreateV6) isPrivateIpStackCreateVariant() {} + // PrivateIpStackCreateValue is the type definition for a PrivateIpStackCreateValue. // // Required fields: @@ -5594,22 +5836,75 @@ type PrivateIpStackCreateValue struct { V6 PrivateIpv6StackCreate `json:"v6" yaml:"v6"` } -// PrivateIpStackCreateDualStack is the interface has both an IPv4 and IPv6 stack. -// -// Required fields: -// - Type -// - Value +// PrivateIpStackCreateDualStack is a variant of PrivateIpStackCreate. type PrivateIpStackCreateDualStack struct { - Type PrivateIpStackCreateType `json:"type" yaml:"type"` Value PrivateIpStackCreateValue `json:"value" yaml:"value"` } +func (PrivateIpStackCreateDualStack) isPrivateIpStackCreateVariant() {} + // PrivateIpStackCreate is create parameters for a network interface's IP stack. type PrivateIpStackCreate struct { - // Type is the type definition for a Type. - Type PrivateIpStackCreateType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is configuration for a network interface's IPv4 addressing. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value privateIpStackCreateVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v PrivateIpStackCreate) Type() PrivateIpStackCreateType { + switch v.Value.(type) { + case *PrivateIpStackCreateV4: + return PrivateIpStackCreateTypeV4 + case *PrivateIpStackCreateV6: + return PrivateIpStackCreateTypeV6 + case *PrivateIpStackCreateDualStack: + return PrivateIpStackCreateTypeDualStack + default: + return "" + } +} + +func (v *PrivateIpStackCreate) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value privateIpStackCreateVariant + switch d.Type { + case "v4": + value = &PrivateIpStackCreateV4{} + case "v6": + value = &PrivateIpStackCreateV6{} + case "dual_stack": + value = &PrivateIpStackCreateDualStack{} + default: + return fmt.Errorf("unknown variant %q, expected 'v4' or 'v6' or 'dual_stack'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v PrivateIpStackCreate) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // PrivateIpv4Config is vPC-private IPv4 configuration for a network interface. @@ -5940,147 +6235,255 @@ type RouteConfig struct { Routes []Route `json:"routes" yaml:"routes"` } +// routeDestinationVariant is implemented by RouteDestination variants. +type routeDestinationVariant interface { + isRouteDestinationVariant() +} + // RouteDestinationType is the type definition for a RouteDestinationType. type RouteDestinationType string -// RouteDestinationIp is route applies to traffic destined for the specified IP address -// -// Required fields: -// - Type -// - Value +// RouteDestinationIp is a variant of RouteDestination. type RouteDestinationIp struct { - Type RouteDestinationType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// RouteDestinationIpNet is route applies to traffic destined for the specified IP subnet -// -// Required fields: -// - Type -// - Value +func (RouteDestinationIp) isRouteDestinationVariant() {} + +// RouteDestinationIpNet is a variant of RouteDestination. type RouteDestinationIpNet struct { - Type RouteDestinationType `json:"type" yaml:"type"` - Value IpNet `json:"value" yaml:"value"` + Value IpNet `json:"value" yaml:"value"` } -// RouteDestinationVpc is route applies to traffic destined for the specified VPC -// -// Required fields: -// - Type -// - Value +func (RouteDestinationIpNet) isRouteDestinationVariant() {} + +// RouteDestinationVpc is a variant of RouteDestination. type RouteDestinationVpc struct { - Type RouteDestinationType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// RouteDestinationSubnet is route applies to traffic destined for the specified VPC subnet -// -// Required fields: -// - Type -// - Value +func (RouteDestinationVpc) isRouteDestinationVariant() {} + +// RouteDestinationSubnet is a variant of RouteDestination. type RouteDestinationSubnet struct { - Type RouteDestinationType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } +func (RouteDestinationSubnet) isRouteDestinationVariant() {} + // RouteDestination is a `RouteDestination` is used to match traffic with a routing rule based on the destination // of that traffic. // // When traffic is to be sent to a destination that is within a given `RouteDestination`, the corresponding `RouterRoute` // applies, and traffic will be forward to the `RouteTarget` for that rule. type RouteDestination struct { - // Type is the type definition for a Type. - Type RouteDestinationType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is the type definition for a Value. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value routeDestinationVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v RouteDestination) Type() RouteDestinationType { + switch v.Value.(type) { + case *RouteDestinationIp: + return RouteDestinationTypeIp + case *RouteDestinationIpNet: + return RouteDestinationTypeIpNet + case *RouteDestinationVpc: + return RouteDestinationTypeVpc + case *RouteDestinationSubnet: + return RouteDestinationTypeSubnet + default: + return "" + } +} + +func (v *RouteDestination) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value routeDestinationVariant + switch d.Type { + case "ip": + value = &RouteDestinationIp{} + case "ip_net": + value = &RouteDestinationIpNet{} + case "vpc": + value = &RouteDestinationVpc{} + case "subnet": + value = &RouteDestinationSubnet{} + default: + return fmt.Errorf("unknown variant %q, expected 'ip' or 'ip_net' or 'vpc' or 'subnet'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v RouteDestination) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) +} + +// routeTargetVariant is implemented by RouteTarget variants. +type routeTargetVariant interface { + isRouteTargetVariant() } // RouteTargetType is the type definition for a RouteTargetType. type RouteTargetType string -// RouteTargetIp is forward traffic to a particular IP address. -// -// Required fields: -// - Type -// - Value +// RouteTargetIp is a variant of RouteTarget. type RouteTargetIp struct { - Type RouteTargetType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// RouteTargetVpc is forward traffic to a VPC -// -// Required fields: -// - Type -// - Value +func (RouteTargetIp) isRouteTargetVariant() {} + +// RouteTargetVpc is a variant of RouteTarget. type RouteTargetVpc struct { - Type RouteTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// RouteTargetSubnet is forward traffic to a VPC Subnet -// -// Required fields: -// - Type -// - Value +func (RouteTargetVpc) isRouteTargetVariant() {} + +// RouteTargetSubnet is a variant of RouteTarget. type RouteTargetSubnet struct { - Type RouteTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// RouteTargetInstance is forward traffic to a specific instance -// -// Required fields: -// - Type -// - Value +func (RouteTargetSubnet) isRouteTargetVariant() {} + +// RouteTargetInstance is a variant of RouteTarget. type RouteTargetInstance struct { - Type RouteTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// RouteTargetInternetGateway is forward traffic to an internet gateway -// -// Required fields: -// - Type -// - Value +func (RouteTargetInstance) isRouteTargetVariant() {} + +// RouteTargetInternetGateway is a variant of RouteTarget. type RouteTargetInternetGateway struct { - Type RouteTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// RouteTargetDrop is drop matching traffic -// -// Required fields: -// - Type -type RouteTargetDrop struct { - Type RouteTargetType `json:"type" yaml:"type"` -} +func (RouteTargetInternetGateway) isRouteTargetVariant() {} + +// RouteTargetDrop is a variant of RouteTarget. +type RouteTargetDrop struct{} + +func (RouteTargetDrop) isRouteTargetVariant() {} // RouteTarget is a `RouteTarget` describes the possible locations that traffic matching a route destination can // be sent. type RouteTarget struct { - // Type is the type definition for a Type. - Type RouteTargetType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is the type definition for a Value. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value routeTargetVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v RouteTarget) Type() RouteTargetType { + switch v.Value.(type) { + case *RouteTargetIp: + return RouteTargetTypeIp + case *RouteTargetVpc: + return RouteTargetTypeVpc + case *RouteTargetSubnet: + return RouteTargetTypeSubnet + case *RouteTargetInstance: + return RouteTargetTypeInstance + case *RouteTargetInternetGateway: + return RouteTargetTypeInternetGateway + case *RouteTargetDrop: + return RouteTargetTypeDrop + default: + return "" + } +} + +func (v *RouteTarget) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value routeTargetVariant + switch d.Type { + case "ip": + value = &RouteTargetIp{} + case "vpc": + value = &RouteTargetVpc{} + case "subnet": + value = &RouteTargetSubnet{} + case "instance": + value = &RouteTargetInstance{} + case "internet_gateway": + value = &RouteTargetInternetGateway{} + case "drop": + value = &RouteTargetDrop{} + default: + return fmt.Errorf("unknown variant %q, expected 'ip' or 'vpc' or 'subnet' or 'instance' or 'internet_gateway' or 'drop'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v RouteTarget) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // RouterRoute is a route defines a rule that governs where traffic should be sent based on its destination. @@ -7732,77 +8135,132 @@ type Utilization struct { Provisioned VirtualResourceCounts `json:"provisioned" yaml:"provisioned"` } +// valueArrayVariant is implemented by ValueArray variants. +type valueArrayVariant interface { + isValueArrayVariant() +} + // ValueArrayType is the type definition for a ValueArrayType. type ValueArrayType string -// ValueArrayInteger is the type definition for a ValueArrayInteger. -// -// Required fields: -// - Type -// - Values +// ValueArrayInteger is a variant of ValueArray. type ValueArrayInteger struct { - Type ValueArrayType `json:"type" yaml:"type"` - Values []int `json:"values" yaml:"values"` + Values []int `json:"values" yaml:"values"` } -// ValueArrayDouble is the type definition for a ValueArrayDouble. -// -// Required fields: -// - Type -// - Values +func (ValueArrayInteger) isValueArrayVariant() {} + +// ValueArrayDouble is a variant of ValueArray. type ValueArrayDouble struct { - Type ValueArrayType `json:"type" yaml:"type"` - Values []float64 `json:"values" yaml:"values"` + Values []float64 `json:"values" yaml:"values"` } -// ValueArrayBoolean is the type definition for a ValueArrayBoolean. -// -// Required fields: -// - Type -// - Values +func (ValueArrayDouble) isValueArrayVariant() {} + +// ValueArrayBoolean is a variant of ValueArray. type ValueArrayBoolean struct { - Type ValueArrayType `json:"type" yaml:"type"` - Values []bool `json:"values" yaml:"values"` + Values []bool `json:"values" yaml:"values"` } -// ValueArrayString is the type definition for a ValueArrayString. -// -// Required fields: -// - Type -// - Values +func (ValueArrayBoolean) isValueArrayVariant() {} + +// ValueArrayString is a variant of ValueArray. type ValueArrayString struct { - Type ValueArrayType `json:"type" yaml:"type"` - Values []string `json:"values" yaml:"values"` + Values []string `json:"values" yaml:"values"` } -// ValueArrayIntegerDistribution is the type definition for a ValueArrayIntegerDistribution. -// -// Required fields: -// - Type -// - Values +func (ValueArrayString) isValueArrayVariant() {} + +// ValueArrayIntegerDistribution is a variant of ValueArray. type ValueArrayIntegerDistribution struct { - Type ValueArrayType `json:"type" yaml:"type"` Values []Distributionint64 `json:"values" yaml:"values"` } -// ValueArrayDoubleDistribution is the type definition for a ValueArrayDoubleDistribution. -// -// Required fields: -// - Type -// - Values +func (ValueArrayIntegerDistribution) isValueArrayVariant() {} + +// ValueArrayDoubleDistribution is a variant of ValueArray. type ValueArrayDoubleDistribution struct { - Type ValueArrayType `json:"type" yaml:"type"` Values []Distributiondouble `json:"values" yaml:"values"` } +func (ValueArrayDoubleDistribution) isValueArrayVariant() {} + // ValueArray is list of data values for one timeseries. // // Each element is an option, where `None` represents a missing sample. type ValueArray struct { - // Type is the type definition for a Type. - Type ValueArrayType `json:"type,omitempty" yaml:"type,omitempty"` - // Values is the type definition for a Values. - Values any `json:"values,omitempty" yaml:"values,omitempty"` + Values valueArrayVariant `json:"values,omitempty" yaml:"values,omitempty"` +} + +func (v ValueArray) Type() ValueArrayType { + switch v.Values.(type) { + case *ValueArrayInteger: + return ValueArrayTypeInteger + case *ValueArrayDouble: + return ValueArrayTypeDouble + case *ValueArrayBoolean: + return ValueArrayTypeBoolean + case *ValueArrayString: + return ValueArrayTypeString + case *ValueArrayIntegerDistribution: + return ValueArrayTypeIntegerDistribution + case *ValueArrayDoubleDistribution: + return ValueArrayTypeDoubleDistribution + default: + return "" + } +} + +func (v *ValueArray) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value valueArrayVariant + switch d.Type { + case "integer": + value = &ValueArrayInteger{} + case "double": + value = &ValueArrayDouble{} + case "boolean": + value = &ValueArrayBoolean{} + case "string": + value = &ValueArrayString{} + case "integer_distribution": + value = &ValueArrayIntegerDistribution{} + case "double_distribution": + value = &ValueArrayDoubleDistribution{} + default: + return fmt.Errorf("unknown variant %q, expected 'integer' or 'double' or 'boolean' or 'string' or 'integer_distribution' or 'double_distribution'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Values = value + return nil +} + +func (v ValueArray) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Values != nil { + valueBytes, err := json.Marshal(v.Values) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // Values is a single list of values, for one dimension of a timeseries. @@ -7964,77 +8422,129 @@ type VpcFirewallRuleFilter struct { Protocols []VpcFirewallRuleProtocol `json:"protocols" yaml:"protocols"` } +// vpcFirewallRuleHostFilterVariant is implemented by VpcFirewallRuleHostFilter variants. +type vpcFirewallRuleHostFilterVariant interface { + isVpcFirewallRuleHostFilterVariant() +} + // VpcFirewallRuleHostFilterType is the type definition for a VpcFirewallRuleHostFilterType. type VpcFirewallRuleHostFilterType string -// VpcFirewallRuleHostFilterVpc is the rule applies to traffic from/to all instances in the VPC -// -// Required fields: -// - Type -// - Value +// VpcFirewallRuleHostFilterVpc is a variant of VpcFirewallRuleHostFilter. type VpcFirewallRuleHostFilterVpc struct { - Type VpcFirewallRuleHostFilterType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleHostFilterSubnet is the rule applies to traffic from/to all instances in the VPC Subnet -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleHostFilterVpc) isVpcFirewallRuleHostFilterVariant() {} + +// VpcFirewallRuleHostFilterSubnet is a variant of VpcFirewallRuleHostFilter. type VpcFirewallRuleHostFilterSubnet struct { - Type VpcFirewallRuleHostFilterType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleHostFilterInstance is the rule applies to traffic from/to this specific instance -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleHostFilterSubnet) isVpcFirewallRuleHostFilterVariant() {} + +// VpcFirewallRuleHostFilterInstance is a variant of VpcFirewallRuleHostFilter. type VpcFirewallRuleHostFilterInstance struct { - Type VpcFirewallRuleHostFilterType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleHostFilterIp is the rule applies to traffic from/to a specific IP address -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleHostFilterInstance) isVpcFirewallRuleHostFilterVariant() {} + +// VpcFirewallRuleHostFilterIp is a variant of VpcFirewallRuleHostFilter. type VpcFirewallRuleHostFilterIp struct { - Type VpcFirewallRuleHostFilterType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// VpcFirewallRuleHostFilterIpNet is the rule applies to traffic from/to a specific IP subnet -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleHostFilterIp) isVpcFirewallRuleHostFilterVariant() {} + +// VpcFirewallRuleHostFilterIpNet is a variant of VpcFirewallRuleHostFilter. type VpcFirewallRuleHostFilterIpNet struct { - Type VpcFirewallRuleHostFilterType `json:"type" yaml:"type"` - Value IpNet `json:"value" yaml:"value"` + Value IpNet `json:"value" yaml:"value"` } +func (VpcFirewallRuleHostFilterIpNet) isVpcFirewallRuleHostFilterVariant() {} + // VpcFirewallRuleHostFilter is the `VpcFirewallRuleHostFilter` is used to filter traffic on the basis of // its source or destination host. type VpcFirewallRuleHostFilter struct { - // Type is the type definition for a Type. - Type VpcFirewallRuleHostFilterType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase - // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They - // can be at most 63 characters long. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value vpcFirewallRuleHostFilterVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v VpcFirewallRuleHostFilter) Type() VpcFirewallRuleHostFilterType { + switch v.Value.(type) { + case *VpcFirewallRuleHostFilterVpc: + return VpcFirewallRuleHostFilterTypeVpc + case *VpcFirewallRuleHostFilterSubnet: + return VpcFirewallRuleHostFilterTypeSubnet + case *VpcFirewallRuleHostFilterInstance: + return VpcFirewallRuleHostFilterTypeInstance + case *VpcFirewallRuleHostFilterIp: + return VpcFirewallRuleHostFilterTypeIp + case *VpcFirewallRuleHostFilterIpNet: + return VpcFirewallRuleHostFilterTypeIpNet + default: + return "" + } +} + +func (v *VpcFirewallRuleHostFilter) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value vpcFirewallRuleHostFilterVariant + switch d.Type { + case "vpc": + value = &VpcFirewallRuleHostFilterVpc{} + case "subnet": + value = &VpcFirewallRuleHostFilterSubnet{} + case "instance": + value = &VpcFirewallRuleHostFilterInstance{} + case "ip": + value = &VpcFirewallRuleHostFilterIp{} + case "ip_net": + value = &VpcFirewallRuleHostFilterIpNet{} + default: + return fmt.Errorf("unknown variant %q, expected 'vpc' or 'subnet' or 'instance' or 'ip' or 'ip_net'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v VpcFirewallRuleHostFilter) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // VpcFirewallRuleProtocolType is the type definition for a VpcFirewallRuleProtocolType. @@ -8077,79 +8587,131 @@ type VpcFirewallRuleProtocol struct { // VpcFirewallRuleStatus is the type definition for a VpcFirewallRuleStatus. type VpcFirewallRuleStatus string +// vpcFirewallRuleTargetVariant is implemented by VpcFirewallRuleTarget variants. +type vpcFirewallRuleTargetVariant interface { + isVpcFirewallRuleTargetVariant() +} + // VpcFirewallRuleTargetType is the type definition for a VpcFirewallRuleTargetType. type VpcFirewallRuleTargetType string -// VpcFirewallRuleTargetVpc is the rule applies to all instances in the VPC -// -// Required fields: -// - Type -// - Value +// VpcFirewallRuleTargetVpc is a variant of VpcFirewallRuleTarget. type VpcFirewallRuleTargetVpc struct { - Type VpcFirewallRuleTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleTargetSubnet is the rule applies to all instances in the VPC Subnet -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleTargetVpc) isVpcFirewallRuleTargetVariant() {} + +// VpcFirewallRuleTargetSubnet is a variant of VpcFirewallRuleTarget. type VpcFirewallRuleTargetSubnet struct { - Type VpcFirewallRuleTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleTargetInstance is the rule applies to this specific instance -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleTargetSubnet) isVpcFirewallRuleTargetVariant() {} + +// VpcFirewallRuleTargetInstance is a variant of VpcFirewallRuleTarget. type VpcFirewallRuleTargetInstance struct { - Type VpcFirewallRuleTargetType `json:"type" yaml:"type"` // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They // can be at most 63 characters long. Value Name `json:"value" yaml:"value"` } -// VpcFirewallRuleTargetIp is the rule applies to a specific IP address -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleTargetInstance) isVpcFirewallRuleTargetVariant() {} + +// VpcFirewallRuleTargetIp is a variant of VpcFirewallRuleTarget. type VpcFirewallRuleTargetIp struct { - Type VpcFirewallRuleTargetType `json:"type" yaml:"type"` - Value string `json:"value" yaml:"value"` + Value string `json:"value" yaml:"value"` } -// VpcFirewallRuleTargetIpNet is the rule applies to a specific IP subnet -// -// Required fields: -// - Type -// - Value +func (VpcFirewallRuleTargetIp) isVpcFirewallRuleTargetVariant() {} + +// VpcFirewallRuleTargetIpNet is a variant of VpcFirewallRuleTarget. type VpcFirewallRuleTargetIpNet struct { - Type VpcFirewallRuleTargetType `json:"type" yaml:"type"` - Value IpNet `json:"value" yaml:"value"` + Value IpNet `json:"value" yaml:"value"` } +func (VpcFirewallRuleTargetIpNet) isVpcFirewallRuleTargetVariant() {} + // VpcFirewallRuleTarget is a `VpcFirewallRuleTarget` is used to specify the set of instances to which a // firewall rule applies. You can target instances directly by name, or specify a VPC, VPC subnet, IP, or IP // subnet, which will apply the rule to traffic going to all matching instances. Targets are additive: the rule // applies to instances matching ANY target. type VpcFirewallRuleTarget struct { - // Type is the type definition for a Type. - Type VpcFirewallRuleTargetType `json:"type,omitempty" yaml:"type,omitempty"` - // Value is names must begin with a lower case ASCII letter, be composed exclusively of lowercase ASCII, uppercase - // ASCII, numbers, and '-', and may not end with a '-'. Names cannot be a UUID, but they may contain a UUID. They - // can be at most 63 characters long. - Value any `json:"value,omitempty" yaml:"value,omitempty"` + Value vpcFirewallRuleTargetVariant `json:"value,omitempty" yaml:"value,omitempty"` +} + +func (v VpcFirewallRuleTarget) Type() VpcFirewallRuleTargetType { + switch v.Value.(type) { + case *VpcFirewallRuleTargetVpc: + return VpcFirewallRuleTargetTypeVpc + case *VpcFirewallRuleTargetSubnet: + return VpcFirewallRuleTargetTypeSubnet + case *VpcFirewallRuleTargetInstance: + return VpcFirewallRuleTargetTypeInstance + case *VpcFirewallRuleTargetIp: + return VpcFirewallRuleTargetTypeIp + case *VpcFirewallRuleTargetIpNet: + return VpcFirewallRuleTargetTypeIpNet + default: + return "" + } +} + +func (v *VpcFirewallRuleTarget) UnmarshalJSON(data []byte) error { + type discriminator struct { + Type string `json:"type"` + } + var d discriminator + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + var value vpcFirewallRuleTargetVariant + switch d.Type { + case "vpc": + value = &VpcFirewallRuleTargetVpc{} + case "subnet": + value = &VpcFirewallRuleTargetSubnet{} + case "instance": + value = &VpcFirewallRuleTargetInstance{} + case "ip": + value = &VpcFirewallRuleTargetIp{} + case "ip_net": + value = &VpcFirewallRuleTargetIpNet{} + default: + return fmt.Errorf("unknown variant %q, expected 'vpc' or 'subnet' or 'instance' or 'ip' or 'ip_net'", d.Type) + } + if err := json.Unmarshal(data, value); err != nil { + return err + } + v.Value = value + return nil +} + +func (v VpcFirewallRuleTarget) MarshalJSON() ([]byte, error) { + m := make(map[string]any) + m["type"] = v.Type() + if v.Value != nil { + valueBytes, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + var valueMap map[string]any + if err := json.Unmarshal(valueBytes, &valueMap); err != nil { + return nil, err + } + for k, val := range valueMap { + m[k] = val + } + } + return json.Marshal(m) } // VpcFirewallRuleUpdate is a single rule in a VPC firewall From 2c117ce6e80a639a9ab616db9270f0cf3728437e Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Thu, 15 Jan 2026 12:15:27 -0500 Subject: [PATCH 2/6] Refactor tagged union implementation. Restructure intermediate representations of tagged unions for clarity. --- internal/generate/templates/type.go.tpl | 36 ++++---- internal/generate/types.go | 107 ++++++++++++++---------- internal/generate/types_test.go | 54 ++++++------ 3 files changed, 110 insertions(+), 87 deletions(-) diff --git a/internal/generate/templates/type.go.tpl b/internal/generate/templates/type.go.tpl index f31b175..b3091b1 100644 --- a/internal/generate/templates/type.go.tpl +++ b/internal/generate/templates/type.go.tpl @@ -1,7 +1,7 @@ {{splitDocString .Description}} {{- if eq .Type "interface"}} type {{.Name}} interface { - {{.OneOfMarker}}() + {{.VariantMarker.Method}}() } {{else if .Fields}} @@ -14,17 +14,17 @@ type {{.Name}} {{.Type}} { {{- end}} } -{{- if .OneOfMarker}} +{{- if .VariantMarker}} -func ({{.Name}}) {{.OneOfMarker}}() {} +func ({{.Name}}) {{.VariantMarker.Method}}() {} {{- end}} -{{- if .OneOfDiscriminator}} +{{- if .Variants}} -func (v {{.Name}}) {{.OneOfDiscriminatorMethod}}() {{.OneOfDiscriminatorType}} { - switch v.{{.OneOfValueFieldName}}.(type) { - {{- range .OneOfVariants}} +func (v {{.Name}}) {{.Variants.DiscriminatorMethod}}() {{.Variants.DiscriminatorType}} { + switch v.{{.Variants.ValueFieldName}}.(type) { + {{- range .Variants.Variants}} case *{{.TypeName}}: - return {{$.OneOfDiscriminatorType}}{{.DiscriminatorEnumValue}} + return {{$.Variants.DiscriminatorType}}{{.DiscriminatorEnumValue}} {{- end}} default: return "" @@ -33,34 +33,34 @@ func (v {{.Name}}) {{.OneOfDiscriminatorMethod}}() {{.OneOfDiscriminatorType}} { func (v *{{.Name}}) UnmarshalJSON(data []byte) error { type discriminator struct { - Type string `json:"{{.OneOfDiscriminator}}"` + Type string `json:"{{.Variants.Discriminator}}"` } var d discriminator if err := json.Unmarshal(data, &d); err != nil { return err } - var value {{.OneOfVariantType}} + var value {{.Variants.VariantType}} switch d.Type { - {{- range .OneOfVariants}} + {{- range .Variants.Variants}} case "{{.DiscriminatorValue}}": value = &{{.TypeName}}{} {{- end}} default: - return fmt.Errorf("unknown variant %q, expected {{range $i, $v := .OneOfVariants}}{{if $i}} or {{end}}'{{.DiscriminatorValue}}'{{end}}", d.Type) + return fmt.Errorf("unknown variant %q, expected {{range $i, $v := .Variants.Variants}}{{if $i}} or {{end}}'{{.DiscriminatorValue}}'{{end}}", d.Type) } if err := json.Unmarshal(data, value); err != nil { return err } - v.{{.OneOfValueFieldName}} = value + v.{{.Variants.ValueFieldName}} = value return nil } func (v {{.Name}}) MarshalJSON() ([]byte, error) { m := make(map[string]any) - m["{{.OneOfDiscriminator}}"] = v.{{.OneOfDiscriminatorMethod}}() - if v.{{.OneOfValueFieldName}} != nil { - valueBytes, err := json.Marshal(v.{{.OneOfValueFieldName}}) + m["{{.Variants.Discriminator}}"] = v.{{.Variants.DiscriminatorMethod}}() + if v.{{.Variants.ValueFieldName}} != nil { + valueBytes, err := json.Marshal(v.{{.Variants.ValueFieldName}}) if err != nil { return nil, err } @@ -76,10 +76,10 @@ func (v {{.Name}}) MarshalJSON() ([]byte, error) { } {{- end}} -{{else if and (eq .Type "struct") .OneOfMarker}} +{{else if and (eq .Type "struct") .VariantMarker}} type {{.Name}} struct{} -func ({{.Name}}) {{.OneOfMarker}}() {} +func ({{.Name}}) {{.VariantMarker.Method}}() {} {{else}} type {{.Name}} {{.Type}} diff --git a/internal/generate/types.go b/internal/generate/types.go index 4752d5c..8939433 100644 --- a/internal/generate/types.go +++ b/internal/generate/types.go @@ -60,30 +60,43 @@ type TypeTemplate struct { Type string // Fields holds the information for the field Fields []TypeField - // OneOfMarker is the marker method name for interface types (e.g., "isAddressSelectorVariant") - OneOfMarker string - // OneOfMarkerType is the interface type name for variant structs (e.g., - // "addressSelectorVariant") - OneOfMarkerType string - // OneOfDiscriminator is the discriminator property name for oneOf types (e.g., "type") - OneOfDiscriminator string - // OneOfDiscriminatorMethod is the method name that returns the discriminator (e.g., "Type") - OneOfDiscriminatorMethod string - // OneOfDiscriminatorType is the type of the discriminator (e.g., "FieldValueType") - OneOfDiscriminatorType string - // OneOfValueField is the value property name for oneOf types (e.g., "value") - OneOfValueField string - // OneOfValueFieldName is the Go field name for the value field (e.g., "Value") - OneOfValueFieldName string - // OneOfVariantType is the interface type for the Value field (e.g., "addressSelectorVariant") - OneOfVariantType string - // OneOfVariants holds discriminator value -> variant type name mapping for JSON - // marshal/unmarshal - OneOfVariants []OneOfVariant + // VariantMarker is set when this type is part of a tagged union: either the marker + // interface itself (e.g., privateIpStackVariant) or a variant struct that implements + // it (e.g., PrivateIpStackV4, PrivateIpStackV6, PrivateIpStackDualStack). + VariantMarker *VariantMarker + // Variants is set when this type represents a tagged union (e.g., PrivateIpStack). + Variants *VariantConfig } -// OneOfVariant maps a discriminator value to its variant type -type OneOfVariant struct { +// VariantMarker describes a type that is a variant of a tagged union interface +type VariantMarker struct { + // Method is the marker method name (e.g., "isPrivateIpStackVariant") + Method string + // InterfaceType is the interface type name (e.g., "privateIpStackVariant") + // Empty for the interface type itself, set for variant structs + InterfaceType string +} + +// VariantConfig holds configuration for tagged union types +type VariantConfig struct { + // Discriminator is the JSON property name for the discriminator (e.g., "type") + Discriminator string + // DiscriminatorMethod is the Go method name that returns the discriminator (e.g., "Type") + DiscriminatorMethod string + // DiscriminatorType is the enum type for the discriminator (e.g., "PrivateIpStackType") + DiscriminatorType string + // ValueField is the JSON property name for the value field (e.g., "value") + ValueField string + // ValueFieldName is the Go field name for the value field (e.g., "Value") + ValueFieldName string + // VariantType is the interface type for the Value field (e.g., "privateIpStackVariant") + VariantType string + // Variants holds discriminator value -> variant type name mapping for JSON marshal/unmarshal + Variants []Variant +} + +// Variant maps a discriminator value to its variant type +type Variant struct { DiscriminatorValue string DiscriminatorEnumValue string TypeName string @@ -853,15 +866,15 @@ func createInterfaceOneOf( markerMethod := "is" + typeName + "Variant" interfaceTpl := TypeTemplate{ - Description: fmt.Sprintf("// %s is implemented by %s variants.", interfaceName, typeName), - Name: interfaceName, - Type: "interface", - OneOfMarker: markerMethod, + Description: fmt.Sprintf("// %s is implemented by %s variants.", interfaceName, typeName), + Name: interfaceName, + Type: "interface", + VariantMarker: &VariantMarker{Method: markerMethod}, } typeTpls = append(typeTpls, interfaceTpl) // Collect variant info for the main type's JSON methods - var variants []OneOfVariant + var variants []Variant // Create discriminator enum type and variant wrapper types for _, variantRef := range s.OneOf { @@ -887,7 +900,7 @@ func createInterfaceOneOf( variantTypeName := typeName + strcase.ToCamel(discValue) // Track variant for JSON methods - variants = append(variants, OneOfVariant{ + variants = append(variants, Variant{ DiscriminatorValue: discValue, DiscriminatorEnumValue: strcase.ToCamel(discValue), TypeName: variantTypeName, @@ -922,12 +935,14 @@ func createInterfaceOneOf( } variantTpl := TypeTemplate{ - Description: fmt.Sprintf("// %s is a variant of %s.", variantTypeName, typeName), - Name: variantTypeName, - Type: "struct", - Fields: fields, - OneOfMarker: markerMethod, - OneOfMarkerType: interfaceName, + Description: fmt.Sprintf("// %s is a variant of %s.", variantTypeName, typeName), + Name: variantTypeName, + Type: "struct", + Fields: fields, + VariantMarker: &VariantMarker{ + Method: markerMethod, + InterfaceType: interfaceName, + }, } typeTpls = append(typeTpls, variantTpl) } @@ -943,17 +958,19 @@ func createInterfaceOneOf( } mainTpl := TypeTemplate{ - Description: formatTypeDescription(typeName, s), - Name: typeName, - Type: "struct", - Fields: mainFields, - OneOfDiscriminator: discriminatorKey, - OneOfDiscriminatorMethod: strcase.ToCamel(discriminatorKey), - OneOfDiscriminatorType: discriminatorType, - OneOfValueField: valuePropertyName, - OneOfValueFieldName: strcase.ToCamel(valuePropertyName), - OneOfVariantType: interfaceName, - OneOfVariants: variants, + Description: formatTypeDescription(typeName, s), + Name: typeName, + Type: "struct", + Fields: mainFields, + Variants: &VariantConfig{ + Discriminator: discriminatorKey, + DiscriminatorMethod: strcase.ToCamel(discriminatorKey), + DiscriminatorType: discriminatorType, + ValueField: valuePropertyName, + ValueFieldName: strcase.ToCamel(valuePropertyName), + VariantType: interfaceName, + Variants: variants, + }, } typeTpls = append(typeTpls, mainTpl) diff --git a/internal/generate/types_test.go b/internal/generate/types_test.go index 60b6cc6..469790f 100644 --- a/internal/generate/types_test.go +++ b/internal/generate/types_test.go @@ -522,10 +522,10 @@ func Test_createOneOf(t *testing.T) { wantTypes: []TypeTemplate{ // Interface for variant types { - Description: "// intOrStringVariant is implemented by IntOrString variants.", - Name: "intOrStringVariant", - Type: "interface", - OneOfMarker: "isIntOrStringVariant", + Description: "// intOrStringVariant is implemented by IntOrString variants.", + Name: "intOrStringVariant", + Type: "interface", + VariantMarker: &VariantMarker{Method: "isIntOrStringVariant"}, }, { Description: "// IntOrStringType is the type definition for a IntOrStringType.", @@ -539,8 +539,10 @@ func Test_createOneOf(t *testing.T) { Fields: []TypeField{ {Name: "Value", Type: "*int", MarshalKey: "value", Required: true}, }, - OneOfMarker: "isIntOrStringVariant", - OneOfMarkerType: "intOrStringVariant", + VariantMarker: &VariantMarker{ + Method: "isIntOrStringVariant", + InterfaceType: "intOrStringVariant", + }, }, { Description: "// IntOrStringString is a variant of IntOrString.", @@ -549,8 +551,10 @@ func Test_createOneOf(t *testing.T) { Fields: []TypeField{ {Name: "Value", Type: "string", MarshalKey: "value", Required: true}, }, - OneOfMarker: "isIntOrStringVariant", - OneOfMarkerType: "intOrStringVariant", + VariantMarker: &VariantMarker{ + Method: "isIntOrStringVariant", + InterfaceType: "intOrStringVariant", + }, }, { Description: "// IntOrString is a value that can be an int or a string.", @@ -559,22 +563,24 @@ func Test_createOneOf(t *testing.T) { Fields: []TypeField{ {Name: "Value", Type: "intOrStringVariant", MarshalKey: "value"}, }, - OneOfDiscriminator: "type", - OneOfDiscriminatorMethod: "Type", - OneOfDiscriminatorType: "IntOrStringType", - OneOfValueField: "value", - OneOfValueFieldName: "Value", - OneOfVariantType: "intOrStringVariant", - OneOfVariants: []OneOfVariant{ - { - DiscriminatorValue: "int", - DiscriminatorEnumValue: "Int", - TypeName: "IntOrStringInt", - }, - { - DiscriminatorValue: "string", - DiscriminatorEnumValue: "String", - TypeName: "IntOrStringString", + Variants: &VariantConfig{ + Discriminator: "type", + DiscriminatorMethod: "Type", + DiscriminatorType: "IntOrStringType", + ValueField: "value", + ValueFieldName: "Value", + VariantType: "intOrStringVariant", + Variants: []Variant{ + { + DiscriminatorValue: "int", + DiscriminatorEnumValue: "Int", + TypeName: "IntOrStringInt", + }, + { + DiscriminatorValue: "string", + DiscriminatorEnumValue: "String", + TypeName: "IntOrStringString", + }, }, }, }, From b06d653d05bb88ef7004a541572063968e086614 Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Thu, 15 Jan 2026 17:13:39 -0500 Subject: [PATCH 3/6] Post-refactor cleanup. --- internal/generate/templates/type.go.tpl | 2 +- internal/generate/types.go | 67 ++++++++++++------------- internal/generate/types_test.go | 12 ++--- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/internal/generate/templates/type.go.tpl b/internal/generate/templates/type.go.tpl index b3091b1..3737e65 100644 --- a/internal/generate/templates/type.go.tpl +++ b/internal/generate/templates/type.go.tpl @@ -24,7 +24,7 @@ func (v {{.Name}}) {{.Variants.DiscriminatorMethod}}() {{.Variants.Discriminator switch v.{{.Variants.ValueFieldName}}.(type) { {{- range .Variants.Variants}} case *{{.TypeName}}: - return {{$.Variants.DiscriminatorType}}{{.DiscriminatorEnumValue}} + return {{$.Variants.DiscriminatorType}}{{.TypeSuffix}} {{- end}} default: return "" diff --git a/internal/generate/types.go b/internal/generate/types.go index 8939433..a5bad38 100644 --- a/internal/generate/types.go +++ b/internal/generate/types.go @@ -95,11 +95,14 @@ type VariantConfig struct { Variants []Variant } -// Variant maps a discriminator value to its variant type +// Variant represents a single variant in a tagged union (oneOf with discriminator). type Variant struct { - DiscriminatorValue string - DiscriminatorEnumValue string - TypeName string + // DiscriminatorValue is the raw JSON tag value (e.g., "ip_net"). + DiscriminatorValue string + // TypeSuffix is the Go-cased suffix used in type and enum names (e.g., "IpNet"). + TypeSuffix string + // TypeName is the full Go type name for this variant (e.g., "RouteDestinationIpNet"). + TypeName string } // Render renders the TypeTemplate to a Go type. @@ -850,8 +853,9 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E return createFlatOneOf(s, name, typeName, multiTypeProps) } -// createInterfaceOneOf creates a oneOf type using an interface + variant wrapper types. -// This is used when variants have the same property name but different types. +// createInterfaceOneOf creates types representing a tagged union. We build an interface type to +// represent the `oneOf`, a concrete variant struct for each `oneOf` variant, and a wrapper type +// with a single field of the interface type. func createInterfaceOneOf( s *openapi3.Schema, typeName, discriminatorKey, valuePropertyName string, @@ -861,10 +865,9 @@ func createInterfaceOneOf( discriminatorType := typeName + strcase.ToCamel(discriminatorKey) - // Create the variant interface + // Build the interface type. interfaceName := toLowerFirstLetter(typeName) + "Variant" markerMethod := "is" + typeName + "Variant" - interfaceTpl := TypeTemplate{ Description: fmt.Sprintf("// %s is implemented by %s variants.", interfaceName, typeName), Name: interfaceName, @@ -873,21 +876,19 @@ func createInterfaceOneOf( } typeTpls = append(typeTpls, interfaceTpl) - // Collect variant info for the main type's JSON methods var variants []Variant - - // Create discriminator enum type and variant wrapper types for _, variantRef := range s.OneOf { - // Find the discriminator value for this variant - discRef, ok := variantRef.Value.Properties[discriminatorKey] - if !ok || len(discRef.Value.Enum) != 1 { - continue + discProp, ok := variantRef.Value.Properties[discriminatorKey] + if !ok || len(discProp.Value.Enum) != 1 { + panic(fmt.Sprintf( + "[ERROR] Variant in %s oneOf missing discriminator %q or has invalid enum: %+v", + typeName, discriminatorKey, variantRef.Value, + )) } - discValue := discRef.Value.Enum[0].(string) + discValue := discProp.Value.Enum[0].(string) - // Create enum constant for discriminator enums, tt, et := createStringEnum( - discRef.Value, + discProp.Value, collectEnumStringTypes, discriminatorType, discriminatorType, @@ -896,24 +897,22 @@ func createInterfaceOneOf( typeTpls = append(typeTpls, tt...) enumTpls = append(enumTpls, et...) - // Create variant wrapper type (e.g., AddressSelectorExplicit) variantTypeName := typeName + strcase.ToCamel(discValue) - - // Track variant for JSON methods variants = append(variants, Variant{ - DiscriminatorValue: discValue, - DiscriminatorEnumValue: strcase.ToCamel(discValue), - TypeName: variantTypeName, + DiscriminatorValue: discValue, + TypeSuffix: strcase.ToCamel(discValue), + TypeName: variantTypeName, }) - // Find the value property in this variant - valueRef, hasValue := variantRef.Value.Properties[valuePropertyName] + // Find the value property in this variant. `ok` is false for "unit" variants + // that carry no data: e.g., RouteTarget's "drop" variant is just {"type": "drop"}. + valueRef, ok := variantRef.Value.Properties[valuePropertyName] var fields []TypeField - if hasValue { + if ok { valueType := convertToValidGoType(valuePropertyName, typeName, valueRef) - // If the value is an inline object (not a $ref), generate its type + // If the value is an inline object (not a $ref), generate its type. if valueRef.Ref == "" && valueRef.Value != nil && valueRef.Value.Type != nil && valueRef.Value.Type.Is("object") { inlineTypeName := typeName + strcase.ToCamel(valuePropertyName) @@ -947,9 +946,8 @@ func createInterfaceOneOf( typeTpls = append(typeTpls, variantTpl) } - // Create the main struct with only the interface-typed value field - // The discriminator is derived via a Type() method - mainFields := []TypeField{ + // Create the wrapper struct with only the interface-typed value field. + wrapperFields := []TypeField{ { Name: strcase.ToCamel(valuePropertyName), Type: interfaceName, @@ -957,11 +955,11 @@ func createInterfaceOneOf( }, } - mainTpl := TypeTemplate{ + wrapperTpl := TypeTemplate{ Description: formatTypeDescription(typeName, s), Name: typeName, Type: "struct", - Fields: mainFields, + Fields: wrapperFields, Variants: &VariantConfig{ Discriminator: discriminatorKey, DiscriminatorMethod: strcase.ToCamel(discriminatorKey), @@ -972,13 +970,12 @@ func createInterfaceOneOf( Variants: variants, }, } - typeTpls = append(typeTpls, mainTpl) + typeTpls = append(typeTpls, wrapperTpl) return typeTpls, enumTpls } // createFlatOneOf creates a oneOf type using a flat struct with all properties. -// Multi-type properties become `any`. func createFlatOneOf( s *openapi3.Schema, name, typeName string, diff --git a/internal/generate/types_test.go b/internal/generate/types_test.go index 469790f..fe1dceb 100644 --- a/internal/generate/types_test.go +++ b/internal/generate/types_test.go @@ -572,14 +572,14 @@ func Test_createOneOf(t *testing.T) { VariantType: "intOrStringVariant", Variants: []Variant{ { - DiscriminatorValue: "int", - DiscriminatorEnumValue: "Int", - TypeName: "IntOrStringInt", + DiscriminatorValue: "int", + TypeSuffix: "Int", + TypeName: "IntOrStringInt", }, { - DiscriminatorValue: "string", - DiscriminatorEnumValue: "String", - TypeName: "IntOrStringString", + DiscriminatorValue: "string", + TypeSuffix: "String", + TypeName: "IntOrStringString", }, }, }, From 23c0a6aacad8c782c4f6ea8f534ccfd00cb1a478 Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Tue, 20 Jan 2026 16:51:32 -0500 Subject: [PATCH 4/6] Document wrapper structs design. --- DESIGN.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/DESIGN.md b/DESIGN.md index 2ba3b89..46a5ea9 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -154,6 +154,50 @@ params := oxide.InstanceNetworkInterfaceCreateParams{ } ``` +**Why wrapper structs?** + +We represent each variant type using a wrapper struct, e.g. + +```go +type PrivateIpStackV4 struct { + Value PrivateIpv4Stack `json:"value"` +} +``` + +The wrapper struct implements the marker method for the relevant interface: + +```go +func (PrivateIpStackV4) isPrivateIpStackVariant() {} +``` + +Why use wrapper structs? For some variant types, we could omit the wrapper and implement the marker +method on the wrapped type instead: + +```go +func (PrivateIpv4Stack) isPrivateIpStackVariant() {} +``` + +Primitive types can't implement methods, but we could use type definitions instead: + +```go +type MyPrimitiveVariant string + +func (MyPrimitiveVariant) isMyTypeVariant() {} +``` + +However, this presents a few problems: + +- Some variant types are `interface{}` or `any`. We can't implement methods on `any`. +- Some variant types are represented by pointers to primitive types, like `*bool` or `*int`. We + can't implement methods on pointers to primitive types. + +We could represent some variant types with wrapper structs and others as unwrapped structs or type +definitions. But the complexity of conditionally wrapping variant types is potentially more +confusing to end users than consistent use of wrappers. + +Note: we can reconsider this choice if we're able to drop the use of `interface{}` types and +pointers to primitives for variants, and if we're confident that those cases won't emerge again. + ### Discriminator with multiple value fields When a `oneOf` has a discriminator field and _multiple_ value fields, we use a flat struct that From 94d718d51c9c5e2d47e4a90296de4ba63f497e59 Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Tue, 20 Jan 2026 18:47:57 -0500 Subject: [PATCH 5/6] Add String() helpers. Some of our generated enum variants are effectively all strings. For example, RouteDestinationIp is a struct with a string Value field; RouteDestinationIpNet is a struct with an IpNet Value field, which is also effectively a string; RouteDestinationVpc is a struct with a Name Value field, which is also a string; etc. If an sdk user wants to reduce a RouteDestination to a string, e.g. in the terraform provider, they have to switch over all variants: ```go switch v := dest.Value.(type) { case *oxide.RouteDestinationIp: return v.Value case *oxide.RouteDestinationIpNet: if s, ok := v.Value.(string); ok { return s } return fmt.Sprintf("%v", v.Value), nil case *oxide.RouteDestinationVpc: return string(v.Value) ... ``` To save users the trouble of writing code like this for oneOf types whose variants are all string-like, this patch includes oxide/helpers.go, which provides String() methods for the relevant types. Note that we wrote these methods "manually" (i.e. by prompting an llm) rather than adding them to the generated code because only a few types exhibit this behavior, and we don't want to complicate the generation logic unnecessarily. --- oxide/helpers.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 oxide/helpers.go diff --git a/oxide/helpers.go b/oxide/helpers.go new file mode 100644 index 0000000..3a7da1d --- /dev/null +++ b/oxide/helpers.go @@ -0,0 +1,95 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package oxide + +import "fmt" + +// String returns the string representation of the RouteDestination's value. +// Returns an empty string if no variant is set. +func (v RouteDestination) String() string { + if v.Value == nil { + return "" + } + switch val := v.Value.(type) { + case *RouteDestinationIp: + return val.Value + case *RouteDestinationIpNet: + return fmt.Sprintf("%v", val.Value) + case *RouteDestinationVpc: + return string(val.Value) + case *RouteDestinationSubnet: + return string(val.Value) + default: + panic(fmt.Sprintf("unhandled RouteDestination variant: %T", val)) + } +} + +// String returns the string representation of the VpcFirewallRuleHostFilter's value. +// Returns an empty string if no variant is set. +func (v VpcFirewallRuleHostFilter) String() string { + if v.Value == nil { + return "" + } + switch val := v.Value.(type) { + case *VpcFirewallRuleHostFilterVpc: + return string(val.Value) + case *VpcFirewallRuleHostFilterSubnet: + return string(val.Value) + case *VpcFirewallRuleHostFilterInstance: + return string(val.Value) + case *VpcFirewallRuleHostFilterIp: + return val.Value + case *VpcFirewallRuleHostFilterIpNet: + return fmt.Sprintf("%v", val.Value) + default: + panic(fmt.Sprintf("unhandled VpcFirewallRuleHostFilter variant: %T", val)) + } +} + +// String returns the string representation of the VpcFirewallRuleTarget's value. +// Returns an empty string if no variant is set. +func (v VpcFirewallRuleTarget) String() string { + if v.Value == nil { + return "" + } + switch val := v.Value.(type) { + case *VpcFirewallRuleTargetVpc: + return string(val.Value) + case *VpcFirewallRuleTargetSubnet: + return string(val.Value) + case *VpcFirewallRuleTargetInstance: + return string(val.Value) + case *VpcFirewallRuleTargetIp: + return val.Value + case *VpcFirewallRuleTargetIpNet: + return fmt.Sprintf("%v", val.Value) + default: + panic(fmt.Sprintf("unhandled VpcFirewallRuleTarget variant: %T", val)) + } +} + +// String returns the string representation of the RouteTarget's value. +// Returns an empty string if no variant is set or for Drop targets. +func (v RouteTarget) String() string { + if v.Value == nil { + return "" + } + switch val := v.Value.(type) { + case *RouteTargetIp: + return val.Value + case *RouteTargetVpc: + return string(val.Value) + case *RouteTargetSubnet: + return string(val.Value) + case *RouteTargetInstance: + return string(val.Value) + case *RouteTargetInternetGateway: + return string(val.Value) + case *RouteTargetDrop: + return "" + default: + panic(fmt.Sprintf("unhandled RouteTarget variant: %T", val)) + } +} From 93191cef6f9b5550bb3709a6c38b51150a84fc5f Mon Sep 17 00:00:00 2001 From: Josh Carp Date: Thu, 22 Jan 2026 12:58:03 -0500 Subject: [PATCH 6/6] Add generated `As$Variant()` helpers. Add `As$Variant()` helpers for each variant type to the oneOf wrapper structs. These return ($Variant, ok), using a familiar go idiom. h/t @sudomateo --- internal/generate/templates/type.go.tpl | 10 + oxide/types.go | 525 ++++++++++++++++++++++++ 2 files changed, 535 insertions(+) diff --git a/internal/generate/templates/type.go.tpl b/internal/generate/templates/type.go.tpl index 3737e65..a3352d1 100644 --- a/internal/generate/templates/type.go.tpl +++ b/internal/generate/templates/type.go.tpl @@ -74,6 +74,16 @@ func (v {{.Name}}) MarshalJSON() ([]byte, error) { } return json.Marshal(m) } + +{{- range .Variants.Variants}} + +// As{{.TypeSuffix}} attempts to convert the {{$.Name}} to a {{.TypeName}}. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v {{$.Name}}) As{{.TypeSuffix}}() (*{{.TypeName}}, bool) { + val, ok := v.{{$.Variants.ValueFieldName}}.(*{{.TypeName}}) + return val, ok +} +{{- end}} {{- end}} {{else if and (eq .Type "struct") .VariantMarker}} diff --git a/oxide/types.go b/oxide/types.go index 56c90cd..798d818 100644 --- a/oxide/types.go +++ b/oxide/types.go @@ -2412,6 +2412,202 @@ func (v Datum) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsBool attempts to convert the Datum to a DatumBool. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsBool() (*DatumBool, bool) { + val, ok := v.Datum.(*DatumBool) + return val, ok +} + +// AsI8 attempts to convert the Datum to a DatumI8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsI8() (*DatumI8, bool) { + val, ok := v.Datum.(*DatumI8) + return val, ok +} + +// AsU8 attempts to convert the Datum to a DatumU8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsU8() (*DatumU8, bool) { + val, ok := v.Datum.(*DatumU8) + return val, ok +} + +// AsI16 attempts to convert the Datum to a DatumI16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsI16() (*DatumI16, bool) { + val, ok := v.Datum.(*DatumI16) + return val, ok +} + +// AsU16 attempts to convert the Datum to a DatumU16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsU16() (*DatumU16, bool) { + val, ok := v.Datum.(*DatumU16) + return val, ok +} + +// AsI32 attempts to convert the Datum to a DatumI32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsI32() (*DatumI32, bool) { + val, ok := v.Datum.(*DatumI32) + return val, ok +} + +// AsU32 attempts to convert the Datum to a DatumU32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsU32() (*DatumU32, bool) { + val, ok := v.Datum.(*DatumU32) + return val, ok +} + +// AsI64 attempts to convert the Datum to a DatumI64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsI64() (*DatumI64, bool) { + val, ok := v.Datum.(*DatumI64) + return val, ok +} + +// AsU64 attempts to convert the Datum to a DatumU64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsU64() (*DatumU64, bool) { + val, ok := v.Datum.(*DatumU64) + return val, ok +} + +// AsF32 attempts to convert the Datum to a DatumF32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsF32() (*DatumF32, bool) { + val, ok := v.Datum.(*DatumF32) + return val, ok +} + +// AsF64 attempts to convert the Datum to a DatumF64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsF64() (*DatumF64, bool) { + val, ok := v.Datum.(*DatumF64) + return val, ok +} + +// AsString attempts to convert the Datum to a DatumString. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsString() (*DatumString, bool) { + val, ok := v.Datum.(*DatumString) + return val, ok +} + +// AsBytes attempts to convert the Datum to a DatumBytes. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsBytes() (*DatumBytes, bool) { + val, ok := v.Datum.(*DatumBytes) + return val, ok +} + +// AsCumulativeI64 attempts to convert the Datum to a DatumCumulativeI64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsCumulativeI64() (*DatumCumulativeI64, bool) { + val, ok := v.Datum.(*DatumCumulativeI64) + return val, ok +} + +// AsCumulativeU64 attempts to convert the Datum to a DatumCumulativeU64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsCumulativeU64() (*DatumCumulativeU64, bool) { + val, ok := v.Datum.(*DatumCumulativeU64) + return val, ok +} + +// AsCumulativeF32 attempts to convert the Datum to a DatumCumulativeF32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsCumulativeF32() (*DatumCumulativeF32, bool) { + val, ok := v.Datum.(*DatumCumulativeF32) + return val, ok +} + +// AsCumulativeF64 attempts to convert the Datum to a DatumCumulativeF64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsCumulativeF64() (*DatumCumulativeF64, bool) { + val, ok := v.Datum.(*DatumCumulativeF64) + return val, ok +} + +// AsHistogramI8 attempts to convert the Datum to a DatumHistogramI8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramI8() (*DatumHistogramI8, bool) { + val, ok := v.Datum.(*DatumHistogramI8) + return val, ok +} + +// AsHistogramU8 attempts to convert the Datum to a DatumHistogramU8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramU8() (*DatumHistogramU8, bool) { + val, ok := v.Datum.(*DatumHistogramU8) + return val, ok +} + +// AsHistogramI16 attempts to convert the Datum to a DatumHistogramI16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramI16() (*DatumHistogramI16, bool) { + val, ok := v.Datum.(*DatumHistogramI16) + return val, ok +} + +// AsHistogramU16 attempts to convert the Datum to a DatumHistogramU16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramU16() (*DatumHistogramU16, bool) { + val, ok := v.Datum.(*DatumHistogramU16) + return val, ok +} + +// AsHistogramI32 attempts to convert the Datum to a DatumHistogramI32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramI32() (*DatumHistogramI32, bool) { + val, ok := v.Datum.(*DatumHistogramI32) + return val, ok +} + +// AsHistogramU32 attempts to convert the Datum to a DatumHistogramU32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramU32() (*DatumHistogramU32, bool) { + val, ok := v.Datum.(*DatumHistogramU32) + return val, ok +} + +// AsHistogramI64 attempts to convert the Datum to a DatumHistogramI64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramI64() (*DatumHistogramI64, bool) { + val, ok := v.Datum.(*DatumHistogramI64) + return val, ok +} + +// AsHistogramU64 attempts to convert the Datum to a DatumHistogramU64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramU64() (*DatumHistogramU64, bool) { + val, ok := v.Datum.(*DatumHistogramU64) + return val, ok +} + +// AsHistogramF32 attempts to convert the Datum to a DatumHistogramF32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramF32() (*DatumHistogramF32, bool) { + val, ok := v.Datum.(*DatumHistogramF32) + return val, ok +} + +// AsHistogramF64 attempts to convert the Datum to a DatumHistogramF64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsHistogramF64() (*DatumHistogramF64, bool) { + val, ok := v.Datum.(*DatumHistogramF64) + return val, ok +} + +// AsMissing attempts to convert the Datum to a DatumMissing. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v Datum) AsMissing() (*DatumMissing, bool) { + val, ok := v.Datum.(*DatumMissing) + return val, ok +} + // DerEncodedKeyPair is the type definition for a DerEncodedKeyPair. // // Required fields: @@ -3218,6 +3414,90 @@ func (v FieldValue) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsString attempts to convert the FieldValue to a FieldValueString. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsString() (*FieldValueString, bool) { + val, ok := v.Value.(*FieldValueString) + return val, ok +} + +// AsI8 attempts to convert the FieldValue to a FieldValueI8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsI8() (*FieldValueI8, bool) { + val, ok := v.Value.(*FieldValueI8) + return val, ok +} + +// AsU8 attempts to convert the FieldValue to a FieldValueU8. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsU8() (*FieldValueU8, bool) { + val, ok := v.Value.(*FieldValueU8) + return val, ok +} + +// AsI16 attempts to convert the FieldValue to a FieldValueI16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsI16() (*FieldValueI16, bool) { + val, ok := v.Value.(*FieldValueI16) + return val, ok +} + +// AsU16 attempts to convert the FieldValue to a FieldValueU16. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsU16() (*FieldValueU16, bool) { + val, ok := v.Value.(*FieldValueU16) + return val, ok +} + +// AsI32 attempts to convert the FieldValue to a FieldValueI32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsI32() (*FieldValueI32, bool) { + val, ok := v.Value.(*FieldValueI32) + return val, ok +} + +// AsU32 attempts to convert the FieldValue to a FieldValueU32. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsU32() (*FieldValueU32, bool) { + val, ok := v.Value.(*FieldValueU32) + return val, ok +} + +// AsI64 attempts to convert the FieldValue to a FieldValueI64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsI64() (*FieldValueI64, bool) { + val, ok := v.Value.(*FieldValueI64) + return val, ok +} + +// AsU64 attempts to convert the FieldValue to a FieldValueU64. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsU64() (*FieldValueU64, bool) { + val, ok := v.Value.(*FieldValueU64) + return val, ok +} + +// AsIpAddr attempts to convert the FieldValue to a FieldValueIpAddr. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsIpAddr() (*FieldValueIpAddr, bool) { + val, ok := v.Value.(*FieldValueIpAddr) + return val, ok +} + +// AsUuid attempts to convert the FieldValue to a FieldValueUuid. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsUuid() (*FieldValueUuid, bool) { + val, ok := v.Value.(*FieldValueUuid) + return val, ok +} + +// AsBool attempts to convert the FieldValue to a FieldValueBool. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v FieldValue) AsBool() (*FieldValueBool, bool) { + val, ok := v.Value.(*FieldValueBool) + return val, ok +} + // FinalizeDisk is parameters for finalizing a disk type FinalizeDisk struct { // SnapshotName is if specified a snapshot of the disk will be created with the given name during finalization. If @@ -5693,6 +5973,27 @@ func (v PrivateIpConfig) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsV4 attempts to convert the PrivateIpConfig to a PrivateIpConfigV4. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpConfig) AsV4() (*PrivateIpConfigV4, bool) { + val, ok := v.Value.(*PrivateIpConfigV4) + return val, ok +} + +// AsV6 attempts to convert the PrivateIpConfig to a PrivateIpConfigV6. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpConfig) AsV6() (*PrivateIpConfigV6, bool) { + val, ok := v.Value.(*PrivateIpConfigV6) + return val, ok +} + +// AsDualStack attempts to convert the PrivateIpConfig to a PrivateIpConfigDualStack. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpConfig) AsDualStack() (*PrivateIpConfigDualStack, bool) { + val, ok := v.Value.(*PrivateIpConfigDualStack) + return val, ok +} + // privateIpStackVariant is implemented by PrivateIpStack variants. type privateIpStackVariant interface { isPrivateIpStackVariant() @@ -5800,6 +6101,27 @@ func (v PrivateIpStack) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsV4 attempts to convert the PrivateIpStack to a PrivateIpStackV4. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStack) AsV4() (*PrivateIpStackV4, bool) { + val, ok := v.Value.(*PrivateIpStackV4) + return val, ok +} + +// AsV6 attempts to convert the PrivateIpStack to a PrivateIpStackV6. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStack) AsV6() (*PrivateIpStackV6, bool) { + val, ok := v.Value.(*PrivateIpStackV6) + return val, ok +} + +// AsDualStack attempts to convert the PrivateIpStack to a PrivateIpStackDualStack. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStack) AsDualStack() (*PrivateIpStackDualStack, bool) { + val, ok := v.Value.(*PrivateIpStackDualStack) + return val, ok +} + // privateIpStackCreateVariant is implemented by PrivateIpStackCreate variants. type privateIpStackCreateVariant interface { isPrivateIpStackCreateVariant() @@ -5907,6 +6229,27 @@ func (v PrivateIpStackCreate) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsV4 attempts to convert the PrivateIpStackCreate to a PrivateIpStackCreateV4. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStackCreate) AsV4() (*PrivateIpStackCreateV4, bool) { + val, ok := v.Value.(*PrivateIpStackCreateV4) + return val, ok +} + +// AsV6 attempts to convert the PrivateIpStackCreate to a PrivateIpStackCreateV6. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStackCreate) AsV6() (*PrivateIpStackCreateV6, bool) { + val, ok := v.Value.(*PrivateIpStackCreateV6) + return val, ok +} + +// AsDualStack attempts to convert the PrivateIpStackCreate to a PrivateIpStackCreateDualStack. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v PrivateIpStackCreate) AsDualStack() (*PrivateIpStackCreateDualStack, bool) { + val, ok := v.Value.(*PrivateIpStackCreateDualStack) + return val, ok +} + // PrivateIpv4Config is vPC-private IPv4 configuration for a network interface. // // Required fields: @@ -6349,6 +6692,34 @@ func (v RouteDestination) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsIp attempts to convert the RouteDestination to a RouteDestinationIp. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteDestination) AsIp() (*RouteDestinationIp, bool) { + val, ok := v.Value.(*RouteDestinationIp) + return val, ok +} + +// AsIpNet attempts to convert the RouteDestination to a RouteDestinationIpNet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteDestination) AsIpNet() (*RouteDestinationIpNet, bool) { + val, ok := v.Value.(*RouteDestinationIpNet) + return val, ok +} + +// AsVpc attempts to convert the RouteDestination to a RouteDestinationVpc. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteDestination) AsVpc() (*RouteDestinationVpc, bool) { + val, ok := v.Value.(*RouteDestinationVpc) + return val, ok +} + +// AsSubnet attempts to convert the RouteDestination to a RouteDestinationSubnet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteDestination) AsSubnet() (*RouteDestinationSubnet, bool) { + val, ok := v.Value.(*RouteDestinationSubnet) + return val, ok +} + // routeTargetVariant is implemented by RouteTarget variants. type routeTargetVariant interface { isRouteTargetVariant() @@ -6486,6 +6857,48 @@ func (v RouteTarget) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsIp attempts to convert the RouteTarget to a RouteTargetIp. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsIp() (*RouteTargetIp, bool) { + val, ok := v.Value.(*RouteTargetIp) + return val, ok +} + +// AsVpc attempts to convert the RouteTarget to a RouteTargetVpc. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsVpc() (*RouteTargetVpc, bool) { + val, ok := v.Value.(*RouteTargetVpc) + return val, ok +} + +// AsSubnet attempts to convert the RouteTarget to a RouteTargetSubnet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsSubnet() (*RouteTargetSubnet, bool) { + val, ok := v.Value.(*RouteTargetSubnet) + return val, ok +} + +// AsInstance attempts to convert the RouteTarget to a RouteTargetInstance. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsInstance() (*RouteTargetInstance, bool) { + val, ok := v.Value.(*RouteTargetInstance) + return val, ok +} + +// AsInternetGateway attempts to convert the RouteTarget to a RouteTargetInternetGateway. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsInternetGateway() (*RouteTargetInternetGateway, bool) { + val, ok := v.Value.(*RouteTargetInternetGateway) + return val, ok +} + +// AsDrop attempts to convert the RouteTarget to a RouteTargetDrop. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v RouteTarget) AsDrop() (*RouteTargetDrop, bool) { + val, ok := v.Value.(*RouteTargetDrop) + return val, ok +} + // RouterRoute is a route defines a rule that governs where traffic should be sent based on its destination. // // Required fields: @@ -8263,6 +8676,48 @@ func (v ValueArray) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsInteger attempts to convert the ValueArray to a ValueArrayInteger. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsInteger() (*ValueArrayInteger, bool) { + val, ok := v.Values.(*ValueArrayInteger) + return val, ok +} + +// AsDouble attempts to convert the ValueArray to a ValueArrayDouble. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsDouble() (*ValueArrayDouble, bool) { + val, ok := v.Values.(*ValueArrayDouble) + return val, ok +} + +// AsBoolean attempts to convert the ValueArray to a ValueArrayBoolean. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsBoolean() (*ValueArrayBoolean, bool) { + val, ok := v.Values.(*ValueArrayBoolean) + return val, ok +} + +// AsString attempts to convert the ValueArray to a ValueArrayString. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsString() (*ValueArrayString, bool) { + val, ok := v.Values.(*ValueArrayString) + return val, ok +} + +// AsIntegerDistribution attempts to convert the ValueArray to a ValueArrayIntegerDistribution. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsIntegerDistribution() (*ValueArrayIntegerDistribution, bool) { + val, ok := v.Values.(*ValueArrayIntegerDistribution) + return val, ok +} + +// AsDoubleDistribution attempts to convert the ValueArray to a ValueArrayDoubleDistribution. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v ValueArray) AsDoubleDistribution() (*ValueArrayDoubleDistribution, bool) { + val, ok := v.Values.(*ValueArrayDoubleDistribution) + return val, ok +} + // Values is a single list of values, for one dimension of a timeseries. // // Required fields: @@ -8547,6 +9002,41 @@ func (v VpcFirewallRuleHostFilter) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsVpc attempts to convert the VpcFirewallRuleHostFilter to a VpcFirewallRuleHostFilterVpc. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleHostFilter) AsVpc() (*VpcFirewallRuleHostFilterVpc, bool) { + val, ok := v.Value.(*VpcFirewallRuleHostFilterVpc) + return val, ok +} + +// AsSubnet attempts to convert the VpcFirewallRuleHostFilter to a VpcFirewallRuleHostFilterSubnet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleHostFilter) AsSubnet() (*VpcFirewallRuleHostFilterSubnet, bool) { + val, ok := v.Value.(*VpcFirewallRuleHostFilterSubnet) + return val, ok +} + +// AsInstance attempts to convert the VpcFirewallRuleHostFilter to a VpcFirewallRuleHostFilterInstance. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleHostFilter) AsInstance() (*VpcFirewallRuleHostFilterInstance, bool) { + val, ok := v.Value.(*VpcFirewallRuleHostFilterInstance) + return val, ok +} + +// AsIp attempts to convert the VpcFirewallRuleHostFilter to a VpcFirewallRuleHostFilterIp. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleHostFilter) AsIp() (*VpcFirewallRuleHostFilterIp, bool) { + val, ok := v.Value.(*VpcFirewallRuleHostFilterIp) + return val, ok +} + +// AsIpNet attempts to convert the VpcFirewallRuleHostFilter to a VpcFirewallRuleHostFilterIpNet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleHostFilter) AsIpNet() (*VpcFirewallRuleHostFilterIpNet, bool) { + val, ok := v.Value.(*VpcFirewallRuleHostFilterIpNet) + return val, ok +} + // VpcFirewallRuleProtocolType is the type definition for a VpcFirewallRuleProtocolType. type VpcFirewallRuleProtocolType string @@ -8714,6 +9204,41 @@ func (v VpcFirewallRuleTarget) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// AsVpc attempts to convert the VpcFirewallRuleTarget to a VpcFirewallRuleTargetVpc. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleTarget) AsVpc() (*VpcFirewallRuleTargetVpc, bool) { + val, ok := v.Value.(*VpcFirewallRuleTargetVpc) + return val, ok +} + +// AsSubnet attempts to convert the VpcFirewallRuleTarget to a VpcFirewallRuleTargetSubnet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleTarget) AsSubnet() (*VpcFirewallRuleTargetSubnet, bool) { + val, ok := v.Value.(*VpcFirewallRuleTargetSubnet) + return val, ok +} + +// AsInstance attempts to convert the VpcFirewallRuleTarget to a VpcFirewallRuleTargetInstance. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleTarget) AsInstance() (*VpcFirewallRuleTargetInstance, bool) { + val, ok := v.Value.(*VpcFirewallRuleTargetInstance) + return val, ok +} + +// AsIp attempts to convert the VpcFirewallRuleTarget to a VpcFirewallRuleTargetIp. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleTarget) AsIp() (*VpcFirewallRuleTargetIp, bool) { + val, ok := v.Value.(*VpcFirewallRuleTargetIp) + return val, ok +} + +// AsIpNet attempts to convert the VpcFirewallRuleTarget to a VpcFirewallRuleTargetIpNet. +// Returns the variant and true if the conversion succeeded, nil and false otherwise. +func (v VpcFirewallRuleTarget) AsIpNet() (*VpcFirewallRuleTargetIpNet, bool) { + val, ok := v.Value.(*VpcFirewallRuleTargetIpNet) + return val, ok +} + // VpcFirewallRuleUpdate is a single rule in a VPC firewall // // Required fields: