mirror of
https://github.com/LukeHagar/plexterraform.git
synced 2025-12-06 04:20:52 +00:00
275 lines
8.8 KiB
Go
275 lines
8.8 KiB
Go
// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT.
|
|
|
|
package reflect
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
"github.com/hashicorp/terraform-plugin-framework/attr/xattr"
|
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
|
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
|
)
|
|
|
|
// Struct builds a new struct using the data in `object`, as long as `object`
|
|
// is a `tftypes.Object`. It will take the struct type from `target`, which
|
|
// must be a struct type.
|
|
//
|
|
// The properties on `target` must be tagged with a "tfsdk" label containing
|
|
// the field name to map to that property. Every property must be tagged, and
|
|
// every property must be present in the type of `object`, and all the
|
|
// attributes in the type of `object` must have a corresponding property.
|
|
// Properties that don't map to object attributes must have a `tfsdk:"-"` tag,
|
|
// explicitly defining them as not part of the object. This is to catch typos
|
|
// and other mistakes early.
|
|
//
|
|
// Struct is meant to be called from Into, not directly.
|
|
func Struct(ctx context.Context, typ attr.Type, object tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) {
|
|
var diags diag.Diagnostics
|
|
|
|
// this only works with object values, so make sure that constraint is
|
|
// met
|
|
if target.Kind() != reflect.Struct {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("expected a struct type, got %s", target.Type()),
|
|
}))
|
|
return target, diags
|
|
}
|
|
if !object.Type().Is(tftypes.Object{}) {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("cannot reflect %s into a struct, must be an object", object.Type().String()),
|
|
}))
|
|
return target, diags
|
|
}
|
|
attrsType, ok := typ.(attr.TypeWithAttributeTypes)
|
|
if !ok {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("cannot reflect object using type information provided by %T, %T must be an attr.TypeWithAttributeTypes", typ, typ),
|
|
}))
|
|
return target, diags
|
|
}
|
|
|
|
// collect a map of fields that are in the object passed in
|
|
var objectFields map[string]tftypes.Value
|
|
err := object.As(&objectFields)
|
|
if err != nil {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: err,
|
|
}))
|
|
return target, diags
|
|
}
|
|
|
|
// collect a map of fields that are defined in the tags of the struct
|
|
// passed in
|
|
targetFields, err := getStructTags(ctx, target, path, opts)
|
|
if err != nil {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("error retrieving field names from struct tags: %w", err),
|
|
}))
|
|
return target, diags
|
|
}
|
|
|
|
// we require an exact, 1:1 match of these fields to avoid typos
|
|
// leading to surprises, so let's ensure they have the exact same
|
|
// fields defined
|
|
var objectMissing, targetMissing []string
|
|
for field := range targetFields {
|
|
if _, ok := objectFields[field]; !ok {
|
|
objectMissing = append(objectMissing, field)
|
|
}
|
|
}
|
|
for field := range objectFields {
|
|
if _, ok := targetFields[field]; !ok {
|
|
targetMissing = append(targetMissing, field)
|
|
}
|
|
}
|
|
if len(objectMissing) > 0 || len(targetMissing) > 0 {
|
|
var missing []string
|
|
if len(objectMissing) > 0 {
|
|
missing = append(missing, fmt.Sprintf("Struct defines fields not found in object: %s.", commaSeparatedString(objectMissing)))
|
|
}
|
|
if len(targetMissing) > 0 {
|
|
missing = append(missing, fmt.Sprintf("Object defines fields not found in struct: %s.", commaSeparatedString(targetMissing)))
|
|
}
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("mismatch between struct and object: %s", strings.Join(missing, " ")),
|
|
}))
|
|
return target, diags
|
|
}
|
|
|
|
attrTypes := attrsType.AttributeTypes()
|
|
|
|
// now that we know they match perfectly, fill the struct with the
|
|
// values in the object
|
|
// Fork start
|
|
//result := reflect.New(target.Type()).Elem()
|
|
var result reflect.Value
|
|
if target.CanSet() {
|
|
result = target
|
|
} else {
|
|
result = reflect.New(target.Type()).Elem()
|
|
}
|
|
structType := trueReflectValue(target).Type()
|
|
|
|
for field, structFieldPos := range targetFields {
|
|
attrType, ok := attrTypes[field]
|
|
if !ok {
|
|
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
|
|
Val: object,
|
|
TargetType: target.Type(),
|
|
Err: fmt.Errorf("could not find type information for attribute in supplied attr.Type %T", typ),
|
|
}))
|
|
return target, diags
|
|
}
|
|
|
|
fieldReflected := structType.Field(structFieldPos)
|
|
if opts.SourceType == SourceTypeState && fieldReflected.Tag.Get(`tfPlanOnly`) == "true" {
|
|
// skip explicitly excluded fields
|
|
continue
|
|
}
|
|
// Fork End
|
|
structField := result.Field(structFieldPos)
|
|
fieldVal, fieldValDiags := BuildValue(ctx, attrType, objectFields[field], structField, opts, path.AtName(field))
|
|
diags.Append(fieldValDiags...)
|
|
|
|
if diags.HasError() {
|
|
return target, diags
|
|
}
|
|
structField.Set(fieldVal)
|
|
}
|
|
return result, diags
|
|
}
|
|
|
|
// FromStruct builds an attr.Value as produced by `typ` from the data in `val`.
|
|
// `val` must be a struct type, and must have all its properties tagged and be
|
|
// a 1:1 match with the attributes reported by `typ`. FromStruct will recurse
|
|
// into FromValue for each attribute, using the type of the attribute as
|
|
// reported by `typ`.
|
|
//
|
|
// It is meant to be called through FromValue, not directly.
|
|
func FromStruct(ctx context.Context, typ attr.TypeWithAttributeTypes, val reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) {
|
|
var diags diag.Diagnostics
|
|
objTypes := map[string]tftypes.Type{}
|
|
objValues := map[string]tftypes.Value{}
|
|
|
|
// collect a map of fields that are defined in the tags of the struct
|
|
// passed in
|
|
targetFields, err := getStructTags(ctx, val, path, Options{})
|
|
if err != nil {
|
|
err = fmt.Errorf("error retrieving field names from struct tags: %w", err)
|
|
diags.AddAttributeError(
|
|
path,
|
|
"Value Conversion Error",
|
|
"An unexpected error was encountered trying to convert from struct value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
|
|
)
|
|
return nil, diags
|
|
}
|
|
|
|
attrTypes := typ.AttributeTypes()
|
|
|
|
var objectMissing, structMissing []string
|
|
|
|
for field := range targetFields {
|
|
if _, ok := attrTypes[field]; !ok {
|
|
objectMissing = append(objectMissing, field)
|
|
}
|
|
}
|
|
|
|
for attrName, attrType := range attrTypes {
|
|
if attrType == nil {
|
|
objectMissing = append(objectMissing, attrName)
|
|
}
|
|
|
|
if _, ok := targetFields[attrName]; !ok {
|
|
structMissing = append(structMissing, attrName)
|
|
}
|
|
}
|
|
|
|
if len(objectMissing) > 0 || len(structMissing) > 0 {
|
|
missing := make([]string, 0, len(objectMissing)+len(structMissing))
|
|
|
|
if len(objectMissing) > 0 {
|
|
missing = append(missing, fmt.Sprintf("Struct defines fields not found in object: %s.", commaSeparatedString(objectMissing)))
|
|
}
|
|
|
|
if len(structMissing) > 0 {
|
|
missing = append(missing, fmt.Sprintf("Object defines fields not found in struct: %s.", commaSeparatedString(structMissing)))
|
|
}
|
|
|
|
diags.AddAttributeError(
|
|
path,
|
|
"Value Conversion Error",
|
|
"An unexpected error was encountered trying to convert from struct into an object. "+
|
|
"This is always an error in the provider. Please report the following to the provider developer:\n\n"+
|
|
fmt.Sprintf("Mismatch between struct and object type: %s\n", strings.Join(missing, " "))+
|
|
fmt.Sprintf("Struct: %s\n", val.Type())+
|
|
fmt.Sprintf("Object type: %s", typ),
|
|
)
|
|
|
|
return nil, diags
|
|
}
|
|
|
|
for name, fieldNo := range targetFields {
|
|
path := path.AtName(name)
|
|
fieldValue := val.Field(fieldNo)
|
|
|
|
attrVal, attrValDiags := FromValue(ctx, attrTypes[name], fieldValue.Interface(), path)
|
|
diags.Append(attrValDiags...)
|
|
|
|
if diags.HasError() {
|
|
return nil, diags
|
|
}
|
|
|
|
tfObjVal, err := attrVal.ToTerraformValue(ctx)
|
|
if err != nil {
|
|
return nil, append(diags, toTerraformValueErrorDiag(err, path))
|
|
}
|
|
|
|
if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok {
|
|
diags.Append(typeWithValidate.Validate(ctx, tfObjVal, path)...)
|
|
|
|
if diags.HasError() {
|
|
return nil, diags
|
|
}
|
|
}
|
|
|
|
objValues[name] = tfObjVal
|
|
objTypes[name] = tfObjVal.Type()
|
|
}
|
|
|
|
tfVal := tftypes.NewValue(tftypes.Object{
|
|
AttributeTypes: objTypes,
|
|
}, objValues)
|
|
|
|
if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok {
|
|
diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...)
|
|
|
|
if diags.HasError() {
|
|
return nil, diags
|
|
}
|
|
}
|
|
|
|
ret, err := typ.ValueFromTerraform(ctx, tfVal)
|
|
if err != nil {
|
|
return nil, append(diags, valueFromTerraformErrorDiag(err, path))
|
|
}
|
|
|
|
return ret, diags
|
|
}
|