mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-08 07:03:16 -06:00
d8fbaa7924
The serializeCollectionMemberForHash helper can't be called for the MapType values, because MapType doesn't have a schema.Elem. Instead, we can write the key/value pairs directly to the buffer. This still doesn't allow for nested maps or lists, but we need to define that use case before committing to it here.
123 lines
2.9 KiB
Go
123 lines
2.9 KiB
Go
package schema
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
func SerializeValueForHash(buf *bytes.Buffer, val interface{}, schema *Schema) {
|
|
if val == nil {
|
|
buf.WriteRune(';')
|
|
return
|
|
}
|
|
|
|
switch schema.Type {
|
|
case TypeBool:
|
|
if val.(bool) {
|
|
buf.WriteRune('1')
|
|
} else {
|
|
buf.WriteRune('0')
|
|
}
|
|
case TypeInt:
|
|
buf.WriteString(strconv.Itoa(val.(int)))
|
|
case TypeFloat:
|
|
buf.WriteString(strconv.FormatFloat(val.(float64), 'g', -1, 64))
|
|
case TypeString:
|
|
buf.WriteString(val.(string))
|
|
case TypeList:
|
|
buf.WriteRune('(')
|
|
l := val.([]interface{})
|
|
for _, innerVal := range l {
|
|
serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
|
|
}
|
|
buf.WriteRune(')')
|
|
case TypeMap:
|
|
|
|
m := val.(map[string]interface{})
|
|
var keys []string
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
buf.WriteRune('[')
|
|
for _, k := range keys {
|
|
innerVal := m[k]
|
|
if innerVal == nil {
|
|
continue
|
|
}
|
|
buf.WriteString(k)
|
|
buf.WriteRune(':')
|
|
|
|
switch innerVal := innerVal.(type) {
|
|
case int:
|
|
buf.WriteString(strconv.Itoa(innerVal))
|
|
case float64:
|
|
buf.WriteString(strconv.FormatFloat(innerVal, 'g', -1, 64))
|
|
case string:
|
|
buf.WriteString(innerVal)
|
|
default:
|
|
panic(fmt.Sprintf("unknown value type in TypeMap %T", innerVal))
|
|
}
|
|
|
|
buf.WriteRune(';')
|
|
}
|
|
buf.WriteRune(']')
|
|
case TypeSet:
|
|
buf.WriteRune('{')
|
|
s := val.(*Set)
|
|
for _, innerVal := range s.List() {
|
|
serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
|
|
}
|
|
buf.WriteRune('}')
|
|
default:
|
|
panic("unknown schema type to serialize")
|
|
}
|
|
buf.WriteRune(';')
|
|
}
|
|
|
|
// SerializeValueForHash appends a serialization of the given resource config
|
|
// to the given buffer, guaranteeing deterministic results given the same value
|
|
// and schema.
|
|
//
|
|
// Its primary purpose is as input into a hashing function in order
|
|
// to hash complex substructures when used in sets, and so the serialization
|
|
// is not reversible.
|
|
func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Resource) {
|
|
sm := resource.Schema
|
|
m := val.(map[string]interface{})
|
|
var keys []string
|
|
for k := range sm {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, k := range keys {
|
|
innerSchema := sm[k]
|
|
// Skip attributes that are not user-provided. Computed attributes
|
|
// do not contribute to the hash since their ultimate value cannot
|
|
// be known at plan/diff time.
|
|
if !(innerSchema.Required || innerSchema.Optional) {
|
|
continue
|
|
}
|
|
|
|
buf.WriteString(k)
|
|
buf.WriteRune(':')
|
|
innerVal := m[k]
|
|
SerializeValueForHash(buf, innerVal, innerSchema)
|
|
}
|
|
}
|
|
|
|
func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) {
|
|
switch tElem := elem.(type) {
|
|
case *Schema:
|
|
SerializeValueForHash(buf, val, tElem)
|
|
case *Resource:
|
|
buf.WriteRune('<')
|
|
SerializeResourceForHash(buf, val, tElem)
|
|
buf.WriteString(">;")
|
|
default:
|
|
panic(fmt.Sprintf("invalid element type: %T", tElem))
|
|
}
|
|
}
|