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  }