pkg/terraform/object.go (89 lines of code) (raw):
package terraform
import (
"github.com/ahmetb/go-linq/v3"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
var _ Object = &RootBlock{}
var _ Object = &NestedBlock{}
type Object interface {
EvalContext() cty.Value
}
func ListOfObject[T Object](objs []T) cty.Value {
var values []cty.Value
allTypes := make(map[string]cty.Type)
for _, b := range objs {
value := b.EvalContext()
values = append(values, value)
attributeTypes := value.Type().AttributeTypes()
for n, t := range attributeTypes {
if _, ok := allTypes[n]; !ok {
allTypes[n] = t
continue
}
if !allTypes[n].Equals(t) {
if allTypes[n].IsListType() && t.IsListType() {
allTypes[n] = cty.List(mergeObjectType(allTypes[n].ElementType(), t.ElementType()))
continue
}
allTypes[n] = mergeObjectType(allTypes[n], t)
}
}
}
var allFields []string
linq.From(allTypes).Select(func(i interface{}) interface{} {
return i.(linq.KeyValue).Key
}).ToSlice(&allFields)
finalType := cty.ObjectWithOptionalAttrs(allTypes, allFields)
var convertedValues []cty.Value
for _, v := range values {
cv, err := convert.Convert(v, finalType)
if err != nil {
panic(err)
}
convertedValues = append(convertedValues, cv)
}
if len(convertedValues) == 0 {
return cty.ListValEmpty(finalType)
}
return cty.ListVal(convertedValues)
}
func mergeObjectType(t1, t2 cty.Type) cty.Type {
if t1.IsPrimitiveType() && t2.IsPrimitiveType() {
return t1
}
if t1.IsCollectionType() && t2.IsCollectionType() {
return mergeObjectTypeInCollection(t1, t2)
}
newAttriubtes := make(map[string]cty.Type)
for n, t := range t1.AttributeTypes() {
newAttriubtes[n] = t
}
for n, t := range t2.AttributeTypes() {
if _, ok := newAttriubtes[n]; !ok {
newAttriubtes[n] = t
continue
}
newAttriubtes[n] = mergeObjectType(newAttriubtes[n], t)
}
var allFields []string
for n := range newAttriubtes {
allFields = append(allFields, n)
}
return cty.ObjectWithOptionalAttrs(newAttriubtes, allFields)
}
func mergeObjectTypeInCollection(t1, t2 cty.Type) cty.Type {
if t1.ElementType().IsObjectType() && t2.ElementType().IsObjectType() {
mergedElementType := mergeObjectType(t1.ElementType(), t2.ElementType())
if t1.IsListType() {
return cty.List(mergedElementType)
}
if t1.IsMapType() {
return cty.Map(mergedElementType)
}
if t1.IsSetType() {
return cty.Set(mergedElementType)
}
}
return t1
}