Files
plexterraform/internal/provider/reflect/slice.go

215 lines
6.6 KiB
Go

// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT.
package reflect
import (
"context"
"fmt"
"reflect"
"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"
)
// build a slice of elements, matching the type of `target`, and fill it with
// the data in `val`.
func reflectSlice(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) {
var diags diag.Diagnostics
// this only works with slices, so check that out first
if target.Kind() != reflect.Slice {
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
Val: val,
TargetType: target.Type(),
Err: fmt.Errorf("expected a slice type, got %s", target.Type()),
}))
return target, diags
}
// TODO: check that the val is a list or set or tuple
elemTyper, ok := typ.(attr.TypeWithElementType)
if !ok {
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
Val: val,
TargetType: target.Type(),
Err: fmt.Errorf("cannot reflect %s using type information provided by %T, %T must be an attr.TypeWithElementType", val.Type(), typ, typ),
}))
return target, diags
}
// we need our value to become a list of values so we can iterate over
// them and handle them individually
var values []tftypes.Value
err := val.As(&values)
if err != nil {
diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{
Val: val,
TargetType: target.Type(),
Err: err,
}))
return target, diags
}
// we need to know the type the slice is wrapping
elemType := target.Type().Elem()
elemAttrType := elemTyper.ElementType()
// we want an empty version of the slice
slice := reflect.MakeSlice(target.Type(), 0, len(values))
// go over each of the values passed in, create a Go value of the right
// type for them, and add it to our new slice
for pos, value := range values {
// create a new Go value of the type that can go in the slice
targetValue := reflect.Zero(elemType)
// update our path so we can have nice errors
valPath := path.AtListIndex(pos)
if typ.TerraformType(ctx).Is(tftypes.Set{}) {
attrVal, err := elemAttrType.ValueFromTerraform(ctx, value)
if err != nil {
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert to slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return target, diags
}
valPath = path.AtSetValue(attrVal)
}
// reflect the value into our new target
val, valDiags := BuildValue(ctx, elemAttrType, value, targetValue, opts, valPath)
diags.Append(valDiags...)
if diags.HasError() {
return target, diags
}
// add the new target to our slice
slice = reflect.Append(slice, val)
}
return slice, diags
}
// FromSlice returns an attr.Value as produced by `typ` using the data in
// `val`. `val` must be a slice. `typ` must be an attr.TypeWithElementType or
// attr.TypeWithElementTypes. If the slice is nil, the representation of null
// for `typ` will be returned. Otherwise, FromSlice will recurse into FromValue
// for each element in the slice, using the element type or types defined on
// `typ` to construct values for them.
//
// It is meant to be called through FromValue, not directly.
func FromSlice(ctx context.Context, typ attr.Type, val reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) {
var diags diag.Diagnostics
// TODO: support tuples, which are attr.TypeWithElementTypes
tfType := typ.TerraformType(ctx)
if val.IsNil() {
tfVal := tftypes.NewValue(tfType, nil)
if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...)
if diags.HasError() {
return nil, diags
}
}
attrVal, err := typ.ValueFromTerraform(ctx, tfVal)
if err != nil {
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return nil, diags
}
return attrVal, diags
}
t, ok := typ.(attr.TypeWithElementType)
if !ok {
err := fmt.Errorf("cannot use type %T as schema type %T; %T must be an attr.TypeWithElementType to hold %T", val, typ, typ, val)
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return nil, diags
}
elemType := t.ElementType()
tfElems := make([]tftypes.Value, 0, val.Len())
for i := 0; i < val.Len(); i++ {
// The underlying reflect.Slice is fetched by Index(). For set types,
// the path is value-based instead of index-based. Since there is only
// the index until the value is retrieved, this will pass the
// technically incorrect index-based path at first for framework
// debugging purposes, then correct the path afterwards.
valPath := path.AtListIndex(i)
val, valDiags := FromValue(ctx, elemType, val.Index(i).Interface(), valPath)
diags.Append(valDiags...)
if diags.HasError() {
return nil, diags
}
tfVal, err := val.ToTerraformValue(ctx)
if err != nil {
return nil, append(diags, toTerraformValueErrorDiag(err, path))
}
if tfType.Is(tftypes.Set{}) {
valPath = path.AtSetValue(val)
}
if typeWithValidate, ok := elemType.(xattr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfVal, valPath)...)
if diags.HasError() {
return nil, diags
}
}
tfElems = append(tfElems, tfVal)
}
err := tftypes.ValidateValue(tfType, tfElems)
if err != nil {
return nil, append(diags, validateValueErrorDiag(err, path))
}
tfVal := tftypes.NewValue(tfType, tfElems)
if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...)
if diags.HasError() {
return nil, diags
}
}
attrVal, err := typ.ValueFromTerraform(ctx, tfVal)
if err != nil {
diags.AddAttributeError(
path,
"Value Conversion Error",
"An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
)
return nil, diags
}
return attrVal, diags
}