func convertValuesMapToCtyVal(valMap map[string]cty.Value) (cty.Value, error) {
	valMapAsCty := cty.NilVal
	if valMap != nil && len(valMap) > 0 {
		var err error
		valMapAsCty, err = gocty.ToCtyValue(valMap, generateTypeFromValuesMap(valMap))
		if err != nil {
			return valMapAsCty, errors.WithStackTrace(err)
		}
	}
	return valMapAsCty, nil
}
convertValuesMapToCtyVal takes a map of name - cty.Value pairs and converts to a single cty.Value object.
package config
import (
	"encoding/json"
	"github.com/imdario/mergo"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/function"
	"github.com/zclconf/go-cty/cty/gocty"
	ctyjson "github.com/zclconf/go-cty/cty/json"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
)
func wrapStringSliceToStringAsFuncImpl(
	toWrap func(params []string, trackInclude *TrackInclude, terragruntOptions *options.TerragruntOptions) (string, error),
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
) function.Function {
	return function.New(&function.Spec{
		VarParam: &function.Parameter{Type: cty.String},
		Type:     function.StaticReturnType(cty.String),
		Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
			params, err := ctySliceToStringSlice(args)
			if err != nil {
				return cty.StringVal(""), err
			}
			out, err := toWrap(params, trackInclude, terragruntOptions)
			if err != nil {
				return cty.StringVal(""), err
			}
			return cty.StringVal(out), nil
		},
	})
}
func wrapVoidToStringAsFuncImpl(
	toWrap func(trackInclude *TrackInclude, terragruntOptions *options.TerragruntOptions) (string, error),
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
) function.Function {
	return function.New(&function.Spec{
		Type: function.StaticReturnType(cty.String),
		Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
			out, err := toWrap(trackInclude, terragruntOptions)
			if err != nil {
				return cty.StringVal(""), err
			}
			return cty.StringVal(out), nil
		},
	})
}
func wrapVoidToStringSliceAsFuncImpl(
	toWrap func(trackInclude *TrackInclude, terragruntOptions *options.TerragruntOptions) ([]string, error),
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
) function.Function {
	return function.New(&function.Spec{
		Type: function.StaticReturnType(cty.List(cty.String)),
		Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
			outVals, err := toWrap(trackInclude, terragruntOptions)
			if err != nil || len(outVals) == 0 {
				return cty.ListValEmpty(cty.String), err
			}
			outCtyVals := []cty.Value{}
			for _, val := range outVals {
				outCtyVals = append(outCtyVals, cty.StringVal(val))
			}
			return cty.ListVal(outCtyVals), nil
		},
	})
}
func wrapStaticValueToStringSliceAsFuncImpl(out []string) function.Function {
	return function.New(&function.Spec{
		Type: function.StaticReturnType(cty.List(cty.String)),
		Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
			outVals := []cty.Value{}
			for _, val := range out {
				outVals = append(outVals, cty.StringVal(val))
			}
			return cty.ListVal(outVals), nil
		},
	})
}
func ctySliceToStringSlice(args []cty.Value) ([]string, error) {
	var out []string
	for _, arg := range args {
		if arg.Type() != cty.String {
			return nil, errors.WithStackTrace(InvalidParameterType{Expected: "string", Actual: arg.Type().FriendlyName()})
		}
		out = append(out, arg.AsString())
	}
	return out, nil
}
func deepMergeCtyMaps(target cty.Value, source cty.Value) (*cty.Value, error) {
	outMap := make(map[string]interface{})
	targetMap, err := parseCtyValueToMap(target)
	if err != nil {
		return nil, err
	}
	sourceMap, err := parseCtyValueToMap(source)
	if err != nil {
		return nil, err
	}
	for key, val := range targetMap {
		outMap[key] = val
	}
	if err := mergo.Merge(&outMap, sourceMap, mergo.WithAppendSlice, mergo.WithOverride); err != nil {
		return nil, err
	}
	outCty, err := convertToCtyWithJson(outMap)
	if err != nil {
		return nil, err
	}
	return &outCty, nil
}
func parseCtyValueToMap(value cty.Value) (map[string]interface{}, error) {
	jsonBytes, err := ctyjson.Marshal(value, cty.DynamicPseudoType)
	if err != nil {
		return nil, errors.WithStackTrace(err)
	}
	var ctyJsonOutput CtyJsonOutput
	if err := json.Unmarshal(jsonBytes, &ctyJsonOutput); err != nil {
		return nil, errors.WithStackTrace(err)
	}
	return ctyJsonOutput.Value, nil
}
type CtyJsonOutput struct {
	Value map[string]interface{}
	Type  interface{}
}