github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/schema/serialize.go (about) 1 package schema 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strconv" 8 ) 9 10 func SerializeValueForHash(buf *bytes.Buffer, val interface{}, schema *Schema) { 11 if val == nil { 12 buf.WriteRune(';') 13 return 14 } 15 16 switch schema.Type { 17 case TypeBool: 18 if val.(bool) { 19 buf.WriteRune('1') 20 } else { 21 buf.WriteRune('0') 22 } 23 case TypeInt: 24 buf.WriteString(strconv.Itoa(val.(int))) 25 case TypeFloat: 26 buf.WriteString(strconv.FormatFloat(val.(float64), 'g', -1, 64)) 27 case TypeString: 28 buf.WriteString(val.(string)) 29 case TypeList: 30 buf.WriteRune('(') 31 l := val.([]interface{}) 32 for _, innerVal := range l { 33 serializeCollectionMemberForHash(buf, innerVal, schema.Elem) 34 } 35 buf.WriteRune(')') 36 case TypeMap: 37 38 m := val.(map[string]interface{}) 39 var keys []string 40 for k := range m { 41 keys = append(keys, k) 42 } 43 sort.Strings(keys) 44 buf.WriteRune('[') 45 for _, k := range keys { 46 innerVal := m[k] 47 if innerVal == nil { 48 continue 49 } 50 buf.WriteString(k) 51 buf.WriteRune(':') 52 53 switch innerVal := innerVal.(type) { 54 case bool: 55 buf.WriteString(strconv.FormatBool(innerVal)) 56 case int: 57 buf.WriteString(strconv.Itoa(innerVal)) 58 case float64: 59 buf.WriteString(strconv.FormatFloat(innerVal, 'g', -1, 64)) 60 case string: 61 buf.WriteString(innerVal) 62 default: 63 panic(fmt.Sprintf("unknown value type in TypeMap %T", innerVal)) 64 } 65 66 buf.WriteRune(';') 67 } 68 buf.WriteRune(']') 69 case TypeSet: 70 buf.WriteRune('{') 71 s := val.(*Set) 72 for _, innerVal := range s.List() { 73 serializeCollectionMemberForHash(buf, innerVal, schema.Elem) 74 } 75 buf.WriteRune('}') 76 default: 77 panic("unknown schema type to serialize") 78 } 79 buf.WriteRune(';') 80 } 81 82 // SerializeValueForHash appends a serialization of the given resource config 83 // to the given buffer, guaranteeing deterministic results given the same value 84 // and schema. 85 // 86 // Its primary purpose is as input into a hashing function in order 87 // to hash complex substructures when used in sets, and so the serialization 88 // is not reversible. 89 func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Resource) { 90 if val == nil { 91 return 92 } 93 sm := resource.Schema 94 m := val.(map[string]interface{}) 95 var keys []string 96 for k := range sm { 97 keys = append(keys, k) 98 } 99 sort.Strings(keys) 100 for _, k := range keys { 101 innerSchema := sm[k] 102 // Skip attributes that are not user-provided. Computed attributes 103 // do not contribute to the hash since their ultimate value cannot 104 // be known at plan/diff time. 105 if !(innerSchema.Required || innerSchema.Optional) { 106 continue 107 } 108 109 buf.WriteString(k) 110 buf.WriteRune(':') 111 innerVal := m[k] 112 SerializeValueForHash(buf, innerVal, innerSchema) 113 } 114 } 115 116 func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) { 117 switch tElem := elem.(type) { 118 case *Schema: 119 SerializeValueForHash(buf, val, tElem) 120 case *Resource: 121 buf.WriteRune('<') 122 SerializeResourceForHash(buf, val, tElem) 123 buf.WriteString(">;") 124 default: 125 panic(fmt.Sprintf("invalid element type: %T", tElem)) 126 } 127 }