diff --git a/pkg/responseMapper/mapper.go b/pkg/responseMapper/mapper.go new file mode 100644 index 00000000..22ea5668 --- /dev/null +++ b/pkg/responseMapper/mapper.go @@ -0,0 +1,106 @@ +package responseMapper + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func ToModel(_ context.Context, resp interface{}, model any) error { + modelValue := reflect.ValueOf(model) + if modelValue.Kind() != reflect.Ptr || modelValue.IsNil() { + return fmt.Errorf("model must be a pointer and must not be nil") + } + + responseValue := reflect.ValueOf(resp) + if responseValue.Kind() == reflect.Ptr { + return fmt.Errorf("response must NOT be a pointer") + } + + responseType := reflect.TypeOf(resp) + modelType := reflect.TypeOf(model).Elem() + + mdl := modelValue.Elem() + + for i := 0; i < modelType.NumField(); i++ { + f := modelType.Field(i) + if field, ok := responseType.FieldByName(f.Name); ok { + nameField := mdl.FieldByName(f.Name) + switch f.Type { + case reflect.TypeOf(basetypes.StringValue{}): + var s *string + if nameField.IsValid() && nameField.CanSet() && (field.Type == reflect.TypeOf(s) || field.Type.Kind() == reflect.TypeOf(s).Kind()) { + nameField.Set( + reflect.ValueOf( + types.StringValue( + responseValue.FieldByName(f.Name).Elem().String()))) + } else { + fmt.Printf("[ERR] field types differ: %s vs %s\n", field.Type, reflect.TypeOf(s)) + } + case reflect.TypeOf(basetypes.BoolValue{}): + var s *bool + if nameField.IsValid() && nameField.CanSet() && (field.Type == reflect.TypeOf(s) || field.Type.Kind() == reflect.TypeOf(s).Kind()) { + if !responseValue.FieldByName(f.Name).IsZero() { + nameField.Set( + reflect.ValueOf( + types.BoolValue( + responseValue.FieldByName(f.Name).Elem().Bool(), + ), + ), + ) + } else { + nameField.Set( + reflect.ValueOf( + types.BoolValue( + false, + ), + ), + ) + } + } else { + fmt.Printf("[ERR] field types differ: %s vs %s\n", field.Type, reflect.TypeOf(s)) + } + case reflect.TypeOf(basetypes.Int32Value{}): + case reflect.TypeOf(basetypes.Int64Value{}): + var i32 *int32 + var i64 *int64 + if nameField.IsValid() && nameField.CanSet() && (field.Type.Kind() == reflect.TypeOf(i32).Kind() || field.Type.Kind() == reflect.TypeOf(i64).Kind()) { + if !responseValue.FieldByName(f.Name).IsZero() { + nameField.Set( + reflect.ValueOf( + types.Int64Value( + responseValue.FieldByName(f.Name).Elem().Int(), + ), + ), + ) + } else { + nameField.Set( + reflect.ValueOf( + types.Int64Value( + 0, + ), + ), + ) + } + } else { + fmt.Printf("[ERR] field types differ: %s vs %s or %s\n", field.Type, reflect.TypeOf(i32), reflect.TypeOf(i64)) + } + case reflect.TypeOf(basetypes.ObjectValue{}): + fmt.Printf("have an object: %s\n", f.Name) + resField := responseValue.FieldByName(f.Name) + if !resField.IsZero() { + for z := 0; z < field.Type.Elem().NumField(); z++ { + rF := field.Type.Elem().Field(z) + fmt.Printf("field: %s (%s)\n", rF.Name, rF.Type) + } + } else { + fmt.Printf("field is zero") + } + } + } + } + return nil +} diff --git a/tmp/main.go b/tmp/main.go new file mode 100644 index 00000000..9386ae8a --- /dev/null +++ b/tmp/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/responseMapper" + "github.com/mhenselin/terraform-provider-stackitprivatepreview/tmp/temp" +) + +type Model struct { + Id types.String `tfsdk:"id"` // needed by TF + InstanceId types.String `tfsdk:"instance_id"` + ProjectId types.String `tfsdk:"project_id"` + Name types.String `tfsdk:"name"` + BackupSchedule types.String `tfsdk:"backup_schedule"` + FlavorId types.String `tfsdk:"flavor_id"` + Replicas types.Int64 `tfsdk:"replicas"` + RetentionDays types.Int64 `tfsdk:"retention_days"` + Storage types.Object `tfsdk:"storage"` + Version types.String `tfsdk:"version"` + Region types.String `tfsdk:"region"` + Encryption types.Object `tfsdk:"encryption"` + Network types.Object `tfsdk:"network"` + IsDeletable types.Bool `tfsdk:"is_deletable"` +} + +func Ptr[T any](val T) *T { + return &val +} + +func main() { + + ctx := context.Background() + resp := postgresflexalpha.GetInstanceResponse{ + BackupSchedule: Ptr("backupSchedule"), + FlavorId: Ptr("2.4"), + Id: Ptr("myId"), + IsDeletable: Ptr(true), + Encryption: &postgresflexalpha.InstanceEncryption{ + KekKeyId: Ptr("myKekKeyId"), + KekKeyRingId: nil, + KekKeyVersion: nil, + ServiceAccount: nil, + }, + Name: Ptr("NameFromMe"), + Network: &postgresflexalpha.InstanceNetwork{ + AccessScope: postgresflexalpha.InstanceNetworkGetAccessScopeAttributeType(Ptr("PUBLIC")), + Acl: &[]string{"0.0.0.0/0"}, + InstanceAddress: Ptr("10.0.0.1"), + RouterAddress: Ptr("192.168.0.1"), + }, + Replicas: postgresflexalpha.GetInstanceResponseGetReplicasAttributeType(Ptr(int32(7))), + RetentionDays: Ptr(int64(42)), + Status: postgresflexalpha.GetInstanceResponseGetStatusAttributeType(Ptr("READY")), + Storage: &postgresflexalpha.Storage{ + PerformanceClass: Ptr("class007"), + Size: Ptr(int64(123)), + }, + Version: Ptr("11"), + } + + fmt.Println("======== TEST 1 ========") + enc, _ := basetypes.NewObjectValue(map[string]attr.Type{"id": types.StringType}, map[string]attr.Value{"id": basetypes.NewStringValue("")}) + storage, _ := basetypes.NewObjectValue(map[string]attr.Type{"id": types.StringType}, map[string]attr.Value{"id": basetypes.NewStringValue("")}) + net, _ := basetypes.NewObjectValue(map[string]attr.Type{"id": types.StringType}, map[string]attr.Value{"id": basetypes.NewStringValue("")}) + model := Model{ + Id: basetypes.NewStringValue(""), + InstanceId: basetypes.NewStringValue(""), + ProjectId: basetypes.NewStringValue(""), + Name: basetypes.NewStringValue(""), + BackupSchedule: basetypes.NewStringValue(""), + FlavorId: basetypes.NewStringValue(""), + Replicas: basetypes.NewInt64Value(0), + RetentionDays: basetypes.NewInt64Value(0), + Storage: storage, + Version: basetypes.NewStringValue(""), + Region: basetypes.NewStringValue(""), + Encryption: enc, + Network: net, + } + err := responseMapper.ToModel(ctx, resp, &model) + if err != nil { + panic(err) + } + fmt.Printf("model: %s (%+v)\n", reflect.TypeOf(model), model) + + fmt.Println("======== TEST 2 ========") + model2 := temp.InstanceModel{} + err = responseMapper.ToModel(ctx, resp, &model2) + if err != nil { + panic(err) + } + fmt.Printf("model: %s (%+v)\n", reflect.TypeOf(model2), model2) + +} diff --git a/tmp/temp/instance_resource_gen.go b/tmp/temp/instance_resource_gen.go new file mode 100644 index 00000000..d9db55d9 --- /dev/null +++ b/tmp/temp/instance_resource_gen.go @@ -0,0 +1,1597 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package temp + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func InstanceResourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "backup_schedule": schema.StringAttribute{ + Required: true, + Description: "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.", + MarkdownDescription: "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.", + }, + "encryption": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "kek_key_id": schema.StringAttribute{ + Required: true, + Description: "The encryption-key key identifier", + MarkdownDescription: "The encryption-key key identifier", + }, + "kek_key_ring_id": schema.StringAttribute{ + Required: true, + Description: "The encryption-key keyring identifier", + MarkdownDescription: "The encryption-key keyring identifier", + }, + "kek_key_version": schema.StringAttribute{ + Required: true, + Description: "The encryption-key version", + MarkdownDescription: "The encryption-key version", + }, + "service_account": schema.StringAttribute{ + Required: true, + }, + }, + CustomType: EncryptionType{ + ObjectType: types.ObjectType{ + AttrTypes: EncryptionValue{}.AttributeTypes(ctx), + }, + }, + Optional: true, + Computed: true, + Description: "The configuration for instance's volume and backup storage encryption.\n\n⚠️ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.\n", + MarkdownDescription: "The configuration for instance's volume and backup storage encryption.\n\n⚠️ **Note:** This feature is in private preview. Supplying this object is only permitted for enabled accounts. If your account does not have access, the request will be rejected.\n", + }, + "flavor_id": schema.StringAttribute{ + Required: true, + Description: "The id of the instance flavor.", + MarkdownDescription: "The id of the instance flavor.", + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of the instance.", + MarkdownDescription: "The ID of the instance.", + }, + "instance_id": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The ID of the instance.", + MarkdownDescription: "The ID of the instance.", + }, + "is_deletable": schema.BoolAttribute{ + Computed: true, + Description: "Whether the instance can be deleted or not.", + MarkdownDescription: "Whether the instance can be deleted or not.", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "The name of the instance.", + MarkdownDescription: "The name of the instance.", + }, + "network": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "access_scope": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The access scope of the instance. It defines if the instance is public or airgapped.", + MarkdownDescription: "The access scope of the instance. It defines if the instance is public or airgapped.", + Validators: []validator.String{ + stringvalidator.OneOf( + "PUBLIC", + "SNA", + ), + }, + Default: stringdefault.StaticString("PUBLIC"), + }, + "acl": schema.ListAttribute{ + ElementType: types.StringType, + Required: true, + Description: "List of IPV4 cidr.", + MarkdownDescription: "List of IPV4 cidr.", + }, + "instance_address": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + "router_address": schema.StringAttribute{ + Optional: true, + Computed: true, + }, + }, + CustomType: NetworkType{ + ObjectType: types.ObjectType{ + AttrTypes: NetworkValue{}.AttributeTypes(ctx), + }, + }, + Required: true, + Description: "The access configuration of the instance", + MarkdownDescription: "The access configuration of the instance", + }, + "project_id": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The STACKIT project ID.", + MarkdownDescription: "The STACKIT project ID.", + }, + "region": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The region which should be addressed", + MarkdownDescription: "The region which should be addressed", + Validators: []validator.String{ + stringvalidator.OneOf( + "eu01", + ), + }, + }, + "replicas": schema.Int64Attribute{ + Required: true, + Description: "How many replicas the instance should have.", + MarkdownDescription: "How many replicas the instance should have.", + Validators: []validator.Int64{ + int64validator.OneOf( + 1, + 3, + ), + }, + }, + "retention_days": schema.Int64Attribute{ + Required: true, + Description: "How long backups are retained. The value can only be between 32 and 365 days.", + MarkdownDescription: "How long backups are retained. The value can only be between 32 and 365 days.", + }, + "status": schema.StringAttribute{ + Computed: true, + Description: "The current status of the instance.", + MarkdownDescription: "The current status of the instance.", + }, + "storage": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "performance_class": schema.StringAttribute{ + Required: true, + Description: "The storage class for the storage.", + MarkdownDescription: "The storage class for the storage.", + }, + "size": schema.Int64Attribute{ + Required: true, + Description: "The storage size in Gigabytes.", + MarkdownDescription: "The storage size in Gigabytes.", + }, + }, + CustomType: StorageType{ + ObjectType: types.ObjectType{ + AttrTypes: StorageValue{}.AttributeTypes(ctx), + }, + }, + Required: true, + Description: "The object containing information about the storage size and class.", + MarkdownDescription: "The object containing information about the storage size and class.", + }, + "version": schema.StringAttribute{ + Required: true, + Description: "The Postgres version used for the instance. See [Versions Endpoint](/documentation/postgres-flex-service/version/v3alpha1#tag/Version) for supported version parameters.", + MarkdownDescription: "The Postgres version used for the instance. See [Versions Endpoint](/documentation/postgres-flex-service/version/v3alpha1#tag/Version) for supported version parameters.", + }, + }, + } +} + +type InstanceModel struct { + BackupSchedule types.String `tfsdk:"backup_schedule"` + Encryption EncryptionValue `tfsdk:"encryption"` + FlavorId types.String `tfsdk:"flavor_id"` + Id types.String `tfsdk:"id"` + InstanceId types.String `tfsdk:"instance_id"` + IsDeletable types.Bool `tfsdk:"is_deletable"` + Name types.String `tfsdk:"name"` + Network NetworkValue `tfsdk:"network"` + ProjectId types.String `tfsdk:"project_id"` + Region types.String `tfsdk:"region"` + Replicas types.Int64 `tfsdk:"replicas"` + RetentionDays types.Int64 `tfsdk:"retention_days"` + Status types.String `tfsdk:"status"` + Storage StorageValue `tfsdk:"storage"` + Version types.String `tfsdk:"version"` +} + +var _ basetypes.ObjectTypable = EncryptionType{} + +type EncryptionType struct { + basetypes.ObjectType +} + +func (t EncryptionType) Equal(o attr.Type) bool { + other, ok := o.(EncryptionType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t EncryptionType) String() string { + return "EncryptionType" +} + +func (t EncryptionType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + kekKeyIdAttribute, ok := attributes["kek_key_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_id is missing from object`) + + return nil, diags + } + + kekKeyIdVal, ok := kekKeyIdAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_id expected to be basetypes.StringValue, was: %T`, kekKeyIdAttribute)) + } + + kekKeyRingIdAttribute, ok := attributes["kek_key_ring_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_ring_id is missing from object`) + + return nil, diags + } + + kekKeyRingIdVal, ok := kekKeyRingIdAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_ring_id expected to be basetypes.StringValue, was: %T`, kekKeyRingIdAttribute)) + } + + kekKeyVersionAttribute, ok := attributes["kek_key_version"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_version is missing from object`) + + return nil, diags + } + + kekKeyVersionVal, ok := kekKeyVersionAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_version expected to be basetypes.StringValue, was: %T`, kekKeyVersionAttribute)) + } + + serviceAccountAttribute, ok := attributes["service_account"] + + if !ok { + diags.AddError( + "Attribute Missing", + `service_account is missing from object`) + + return nil, diags + } + + serviceAccountVal, ok := serviceAccountAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`service_account expected to be basetypes.StringValue, was: %T`, serviceAccountAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return EncryptionValue{ + KekKeyId: kekKeyIdVal, + KekKeyRingId: kekKeyRingIdVal, + KekKeyVersion: kekKeyVersionVal, + ServiceAccount: serviceAccountVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewEncryptionValueNull() EncryptionValue { + return EncryptionValue{ + state: attr.ValueStateNull, + } +} + +func NewEncryptionValueUnknown() EncryptionValue { + return EncryptionValue{ + state: attr.ValueStateUnknown, + } +} + +func NewEncryptionValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (EncryptionValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing EncryptionValue Attribute Value", + "While creating a EncryptionValue value, a missing attribute value was detected. "+ + "A EncryptionValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("EncryptionValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid EncryptionValue Attribute Type", + "While creating a EncryptionValue value, an invalid attribute value was detected. "+ + "A EncryptionValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("EncryptionValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("EncryptionValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra EncryptionValue Attribute Value", + "While creating a EncryptionValue value, an extra attribute value was detected. "+ + "A EncryptionValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra EncryptionValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewEncryptionValueUnknown(), diags + } + + kekKeyIdAttribute, ok := attributes["kek_key_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_id is missing from object`) + + return NewEncryptionValueUnknown(), diags + } + + kekKeyIdVal, ok := kekKeyIdAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_id expected to be basetypes.StringValue, was: %T`, kekKeyIdAttribute)) + } + + kekKeyRingIdAttribute, ok := attributes["kek_key_ring_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_ring_id is missing from object`) + + return NewEncryptionValueUnknown(), diags + } + + kekKeyRingIdVal, ok := kekKeyRingIdAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_ring_id expected to be basetypes.StringValue, was: %T`, kekKeyRingIdAttribute)) + } + + kekKeyVersionAttribute, ok := attributes["kek_key_version"] + + if !ok { + diags.AddError( + "Attribute Missing", + `kek_key_version is missing from object`) + + return NewEncryptionValueUnknown(), diags + } + + kekKeyVersionVal, ok := kekKeyVersionAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`kek_key_version expected to be basetypes.StringValue, was: %T`, kekKeyVersionAttribute)) + } + + serviceAccountAttribute, ok := attributes["service_account"] + + if !ok { + diags.AddError( + "Attribute Missing", + `service_account is missing from object`) + + return NewEncryptionValueUnknown(), diags + } + + serviceAccountVal, ok := serviceAccountAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`service_account expected to be basetypes.StringValue, was: %T`, serviceAccountAttribute)) + } + + if diags.HasError() { + return NewEncryptionValueUnknown(), diags + } + + return EncryptionValue{ + KekKeyId: kekKeyIdVal, + KekKeyRingId: kekKeyRingIdVal, + KekKeyVersion: kekKeyVersionVal, + ServiceAccount: serviceAccountVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewEncryptionValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) EncryptionValue { + object, diags := NewEncryptionValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewEncryptionValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t EncryptionType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewEncryptionValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewEncryptionValueUnknown(), nil + } + + if in.IsNull() { + return NewEncryptionValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewEncryptionValueMust(EncryptionValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t EncryptionType) ValueType(ctx context.Context) attr.Value { + return EncryptionValue{} +} + +var _ basetypes.ObjectValuable = EncryptionValue{} + +type EncryptionValue struct { + KekKeyId basetypes.StringValue `tfsdk:"kek_key_id"` + KekKeyRingId basetypes.StringValue `tfsdk:"kek_key_ring_id"` + KekKeyVersion basetypes.StringValue `tfsdk:"kek_key_version"` + ServiceAccount basetypes.StringValue `tfsdk:"service_account"` + state attr.ValueState +} + +func (v EncryptionValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 4) + + var val tftypes.Value + var err error + + attrTypes["kek_key_id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["kek_key_ring_id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["kek_key_version"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["service_account"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 4) + + val, err = v.KekKeyId.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["kek_key_id"] = val + + val, err = v.KekKeyRingId.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["kek_key_ring_id"] = val + + val, err = v.KekKeyVersion.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["kek_key_version"] = val + + val, err = v.ServiceAccount.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["service_account"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v EncryptionValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v EncryptionValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v EncryptionValue) String() string { + return "EncryptionValue" +} + +func (v EncryptionValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + attributeTypes := map[string]attr.Type{ + "kek_key_id": basetypes.StringType{}, + "kek_key_ring_id": basetypes.StringType{}, + "kek_key_version": basetypes.StringType{}, + "service_account": basetypes.StringType{}, + } + + if v.IsNull() { + return types.ObjectNull(attributeTypes), diags + } + + if v.IsUnknown() { + return types.ObjectUnknown(attributeTypes), diags + } + + objVal, diags := types.ObjectValue( + attributeTypes, + map[string]attr.Value{ + "kek_key_id": v.KekKeyId, + "kek_key_ring_id": v.KekKeyRingId, + "kek_key_version": v.KekKeyVersion, + "service_account": v.ServiceAccount, + }) + + return objVal, diags +} + +func (v EncryptionValue) Equal(o attr.Value) bool { + other, ok := o.(EncryptionValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.KekKeyId.Equal(other.KekKeyId) { + return false + } + + if !v.KekKeyRingId.Equal(other.KekKeyRingId) { + return false + } + + if !v.KekKeyVersion.Equal(other.KekKeyVersion) { + return false + } + + if !v.ServiceAccount.Equal(other.ServiceAccount) { + return false + } + + return true +} + +func (v EncryptionValue) Type(ctx context.Context) attr.Type { + return EncryptionType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v EncryptionValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "kek_key_id": basetypes.StringType{}, + "kek_key_ring_id": basetypes.StringType{}, + "kek_key_version": basetypes.StringType{}, + "service_account": basetypes.StringType{}, + } +} + +var _ basetypes.ObjectTypable = NetworkType{} + +type NetworkType struct { + basetypes.ObjectType +} + +func (t NetworkType) Equal(o attr.Type) bool { + other, ok := o.(NetworkType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t NetworkType) String() string { + return "NetworkType" +} + +func (t NetworkType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + accessScopeAttribute, ok := attributes["access_scope"] + + if !ok { + diags.AddError( + "Attribute Missing", + `access_scope is missing from object`) + + return nil, diags + } + + accessScopeVal, ok := accessScopeAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`access_scope expected to be basetypes.StringValue, was: %T`, accessScopeAttribute)) + } + + aclAttribute, ok := attributes["acl"] + + if !ok { + diags.AddError( + "Attribute Missing", + `acl is missing from object`) + + return nil, diags + } + + aclVal, ok := aclAttribute.(basetypes.ListValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`acl expected to be basetypes.ListValue, was: %T`, aclAttribute)) + } + + instanceAddressAttribute, ok := attributes["instance_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `instance_address is missing from object`) + + return nil, diags + } + + instanceAddressVal, ok := instanceAddressAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`instance_address expected to be basetypes.StringValue, was: %T`, instanceAddressAttribute)) + } + + routerAddressAttribute, ok := attributes["router_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `router_address is missing from object`) + + return nil, diags + } + + routerAddressVal, ok := routerAddressAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`router_address expected to be basetypes.StringValue, was: %T`, routerAddressAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return NetworkValue{ + AccessScope: accessScopeVal, + Acl: aclVal, + InstanceAddress: instanceAddressVal, + RouterAddress: routerAddressVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewNetworkValueNull() NetworkValue { + return NetworkValue{ + state: attr.ValueStateNull, + } +} + +func NewNetworkValueUnknown() NetworkValue { + return NetworkValue{ + state: attr.ValueStateUnknown, + } +} + +func NewNetworkValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (NetworkValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing NetworkValue Attribute Value", + "While creating a NetworkValue value, a missing attribute value was detected. "+ + "A NetworkValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("NetworkValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid NetworkValue Attribute Type", + "While creating a NetworkValue value, an invalid attribute value was detected. "+ + "A NetworkValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("NetworkValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("NetworkValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra NetworkValue Attribute Value", + "While creating a NetworkValue value, an extra attribute value was detected. "+ + "A NetworkValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra NetworkValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewNetworkValueUnknown(), diags + } + + accessScopeAttribute, ok := attributes["access_scope"] + + if !ok { + diags.AddError( + "Attribute Missing", + `access_scope is missing from object`) + + return NewNetworkValueUnknown(), diags + } + + accessScopeVal, ok := accessScopeAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`access_scope expected to be basetypes.StringValue, was: %T`, accessScopeAttribute)) + } + + aclAttribute, ok := attributes["acl"] + + if !ok { + diags.AddError( + "Attribute Missing", + `acl is missing from object`) + + return NewNetworkValueUnknown(), diags + } + + aclVal, ok := aclAttribute.(basetypes.ListValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`acl expected to be basetypes.ListValue, was: %T`, aclAttribute)) + } + + instanceAddressAttribute, ok := attributes["instance_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `instance_address is missing from object`) + + return NewNetworkValueUnknown(), diags + } + + instanceAddressVal, ok := instanceAddressAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`instance_address expected to be basetypes.StringValue, was: %T`, instanceAddressAttribute)) + } + + routerAddressAttribute, ok := attributes["router_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `router_address is missing from object`) + + return NewNetworkValueUnknown(), diags + } + + routerAddressVal, ok := routerAddressAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`router_address expected to be basetypes.StringValue, was: %T`, routerAddressAttribute)) + } + + if diags.HasError() { + return NewNetworkValueUnknown(), diags + } + + return NetworkValue{ + AccessScope: accessScopeVal, + Acl: aclVal, + InstanceAddress: instanceAddressVal, + RouterAddress: routerAddressVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewNetworkValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) NetworkValue { + object, diags := NewNetworkValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewNetworkValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t NetworkType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewNetworkValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewNetworkValueUnknown(), nil + } + + if in.IsNull() { + return NewNetworkValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewNetworkValueMust(NetworkValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t NetworkType) ValueType(ctx context.Context) attr.Value { + return NetworkValue{} +} + +var _ basetypes.ObjectValuable = NetworkValue{} + +type NetworkValue struct { + AccessScope basetypes.StringValue `tfsdk:"access_scope"` + Acl basetypes.ListValue `tfsdk:"acl"` + InstanceAddress basetypes.StringValue `tfsdk:"instance_address"` + RouterAddress basetypes.StringValue `tfsdk:"router_address"` + state attr.ValueState +} + +func (v NetworkValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 4) + + var val tftypes.Value + var err error + + attrTypes["access_scope"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["acl"] = basetypes.ListType{ + ElemType: types.StringType, + }.TerraformType(ctx) + attrTypes["instance_address"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["router_address"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 4) + + val, err = v.AccessScope.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["access_scope"] = val + + val, err = v.Acl.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["acl"] = val + + val, err = v.InstanceAddress.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["instance_address"] = val + + val, err = v.RouterAddress.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["router_address"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v NetworkValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v NetworkValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v NetworkValue) String() string { + return "NetworkValue" +} + +func (v NetworkValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + var aclVal basetypes.ListValue + switch { + case v.Acl.IsUnknown(): + aclVal = types.ListUnknown(types.StringType) + case v.Acl.IsNull(): + aclVal = types.ListNull(types.StringType) + default: + var d diag.Diagnostics + aclVal, d = types.ListValue(types.StringType, v.Acl.Elements()) + diags.Append(d...) + } + + if diags.HasError() { + return types.ObjectUnknown(map[string]attr.Type{ + "access_scope": basetypes.StringType{}, + "acl": basetypes.ListType{ + ElemType: types.StringType, + }, + "instance_address": basetypes.StringType{}, + "router_address": basetypes.StringType{}, + }), diags + } + + attributeTypes := map[string]attr.Type{ + "access_scope": basetypes.StringType{}, + "acl": basetypes.ListType{ + ElemType: types.StringType, + }, + "instance_address": basetypes.StringType{}, + "router_address": basetypes.StringType{}, + } + + if v.IsNull() { + return types.ObjectNull(attributeTypes), diags + } + + if v.IsUnknown() { + return types.ObjectUnknown(attributeTypes), diags + } + + objVal, diags := types.ObjectValue( + attributeTypes, + map[string]attr.Value{ + "access_scope": v.AccessScope, + "acl": aclVal, + "instance_address": v.InstanceAddress, + "router_address": v.RouterAddress, + }) + + return objVal, diags +} + +func (v NetworkValue) Equal(o attr.Value) bool { + other, ok := o.(NetworkValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.AccessScope.Equal(other.AccessScope) { + return false + } + + if !v.Acl.Equal(other.Acl) { + return false + } + + if !v.InstanceAddress.Equal(other.InstanceAddress) { + return false + } + + if !v.RouterAddress.Equal(other.RouterAddress) { + return false + } + + return true +} + +func (v NetworkValue) Type(ctx context.Context) attr.Type { + return NetworkType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v NetworkValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "access_scope": basetypes.StringType{}, + "acl": basetypes.ListType{ + ElemType: types.StringType, + }, + "instance_address": basetypes.StringType{}, + "router_address": basetypes.StringType{}, + } +} + +var _ basetypes.ObjectTypable = StorageType{} + +type StorageType struct { + basetypes.ObjectType +} + +func (t StorageType) Equal(o attr.Type) bool { + other, ok := o.(StorageType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t StorageType) String() string { + return "StorageType" +} + +func (t StorageType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + performanceClassAttribute, ok := attributes["performance_class"] + + if !ok { + diags.AddError( + "Attribute Missing", + `performance_class is missing from object`) + + return nil, diags + } + + performanceClassVal, ok := performanceClassAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`performance_class expected to be basetypes.StringValue, was: %T`, performanceClassAttribute)) + } + + sizeAttribute, ok := attributes["size"] + + if !ok { + diags.AddError( + "Attribute Missing", + `size is missing from object`) + + return nil, diags + } + + sizeVal, ok := sizeAttribute.(basetypes.Int64Value) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`size expected to be basetypes.Int64Value, was: %T`, sizeAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return StorageValue{ + PerformanceClass: performanceClassVal, + Size: sizeVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewStorageValueNull() StorageValue { + return StorageValue{ + state: attr.ValueStateNull, + } +} + +func NewStorageValueUnknown() StorageValue { + return StorageValue{ + state: attr.ValueStateUnknown, + } +} + +func NewStorageValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (StorageValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing StorageValue Attribute Value", + "While creating a StorageValue value, a missing attribute value was detected. "+ + "A StorageValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("StorageValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid StorageValue Attribute Type", + "While creating a StorageValue value, an invalid attribute value was detected. "+ + "A StorageValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("StorageValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("StorageValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra StorageValue Attribute Value", + "While creating a StorageValue value, an extra attribute value was detected. "+ + "A StorageValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra StorageValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewStorageValueUnknown(), diags + } + + performanceClassAttribute, ok := attributes["performance_class"] + + if !ok { + diags.AddError( + "Attribute Missing", + `performance_class is missing from object`) + + return NewStorageValueUnknown(), diags + } + + performanceClassVal, ok := performanceClassAttribute.(basetypes.StringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`performance_class expected to be basetypes.StringValue, was: %T`, performanceClassAttribute)) + } + + sizeAttribute, ok := attributes["size"] + + if !ok { + diags.AddError( + "Attribute Missing", + `size is missing from object`) + + return NewStorageValueUnknown(), diags + } + + sizeVal, ok := sizeAttribute.(basetypes.Int64Value) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`size expected to be basetypes.Int64Value, was: %T`, sizeAttribute)) + } + + if diags.HasError() { + return NewStorageValueUnknown(), diags + } + + return StorageValue{ + PerformanceClass: performanceClassVal, + Size: sizeVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewStorageValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) StorageValue { + object, diags := NewStorageValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewStorageValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t StorageType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewStorageValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewStorageValueUnknown(), nil + } + + if in.IsNull() { + return NewStorageValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewStorageValueMust(StorageValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t StorageType) ValueType(ctx context.Context) attr.Value { + return StorageValue{} +} + +var _ basetypes.ObjectValuable = StorageValue{} + +type StorageValue struct { + PerformanceClass basetypes.StringValue `tfsdk:"performance_class"` + Size basetypes.Int64Value `tfsdk:"size"` + state attr.ValueState +} + +func (v StorageValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 2) + + var val tftypes.Value + var err error + + attrTypes["performance_class"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["size"] = basetypes.Int64Type{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 2) + + val, err = v.PerformanceClass.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["performance_class"] = val + + val, err = v.Size.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["size"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v StorageValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v StorageValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v StorageValue) String() string { + return "StorageValue" +} + +func (v StorageValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + attributeTypes := map[string]attr.Type{ + "performance_class": basetypes.StringType{}, + "size": basetypes.Int64Type{}, + } + + if v.IsNull() { + return types.ObjectNull(attributeTypes), diags + } + + if v.IsUnknown() { + return types.ObjectUnknown(attributeTypes), diags + } + + objVal, diags := types.ObjectValue( + attributeTypes, + map[string]attr.Value{ + "performance_class": v.PerformanceClass, + "size": v.Size, + }) + + return objVal, diags +} + +func (v StorageValue) Equal(o attr.Value) bool { + other, ok := o.(StorageValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.PerformanceClass.Equal(other.PerformanceClass) { + return false + } + + if !v.Size.Equal(other.Size) { + return false + } + + return true +} + +func (v StorageValue) Type(ctx context.Context) attr.Type { + return StorageType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v StorageValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "performance_class": basetypes.StringType{}, + "size": basetypes.Int64Type{}, + } +}