github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/jsonformat/structured/map.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package structured 5 6 import ( 7 "github.com/terramate-io/tf/command/jsonformat/structured/attribute_path" 8 ) 9 10 // ChangeMap is a Change that represents a Map or an Object type, and has 11 // converted the relevant interfaces into maps for easier access. 12 type ChangeMap struct { 13 // Before contains the value before the proposed change. 14 Before map[string]interface{} 15 16 // After contains the value after the proposed change. 17 After map[string]interface{} 18 19 // Unknown contains the unknown status of any elements/attributes of this 20 // map/object. 21 Unknown map[string]interface{} 22 23 // BeforeSensitive contains the before sensitive status of any 24 // elements/attributes of this map/object. 25 BeforeSensitive map[string]interface{} 26 27 // AfterSensitive contains the after sensitive status of any 28 // elements/attributes of this map/object. 29 AfterSensitive map[string]interface{} 30 31 // ReplacePaths matches the same attributes in Change exactly. 32 ReplacePaths attribute_path.Matcher 33 34 // RelevantAttributes matches the same attributes in Change exactly. 35 RelevantAttributes attribute_path.Matcher 36 } 37 38 // AsMap converts the Change into an object or map representation by converting 39 // the internal Before, After, Unknown, BeforeSensitive, and AfterSensitive 40 // data structures into generic maps. 41 func (change Change) AsMap() ChangeMap { 42 return ChangeMap{ 43 Before: genericToMap(change.Before), 44 After: genericToMap(change.After), 45 Unknown: genericToMap(change.Unknown), 46 BeforeSensitive: genericToMap(change.BeforeSensitive), 47 AfterSensitive: genericToMap(change.AfterSensitive), 48 ReplacePaths: change.ReplacePaths, 49 RelevantAttributes: change.RelevantAttributes, 50 } 51 } 52 53 // GetChild safely packages up a Change object for the given child, handling 54 // all the cases where the data might be null or a static boolean. 55 func (m ChangeMap) GetChild(key string) Change { 56 before, beforeExplicit := getFromGenericMap(m.Before, key) 57 after, afterExplicit := getFromGenericMap(m.After, key) 58 unknown, _ := getFromGenericMap(m.Unknown, key) 59 beforeSensitive, _ := getFromGenericMap(m.BeforeSensitive, key) 60 afterSensitive, _ := getFromGenericMap(m.AfterSensitive, key) 61 62 return Change{ 63 BeforeExplicit: beforeExplicit, 64 AfterExplicit: afterExplicit, 65 Before: before, 66 After: after, 67 Unknown: unknown, 68 BeforeSensitive: beforeSensitive, 69 AfterSensitive: afterSensitive, 70 ReplacePaths: m.ReplacePaths.GetChildWithKey(key), 71 RelevantAttributes: m.RelevantAttributes.GetChildWithKey(key), 72 } 73 } 74 75 // ExplicitKeys returns the keys in the Before and After, as opposed to AllKeys 76 // which also includes keys from the additional meta structures (like the 77 // sensitive and unknown values). 78 // 79 // This function is useful for processing nested attributes and repeated blocks 80 // where the unknown and sensitive structs contain information about the actual 81 // attributes, while the before and after structs hold the actual nested values. 82 func (m ChangeMap) ExplicitKeys() []string { 83 keys := make(map[string]bool) 84 for before := range m.Before { 85 if _, ok := keys[before]; ok { 86 continue 87 } 88 keys[before] = true 89 } 90 for after := range m.After { 91 if _, ok := keys[after]; ok { 92 continue 93 } 94 keys[after] = true 95 } 96 97 var dedupedKeys []string 98 for key := range keys { 99 dedupedKeys = append(dedupedKeys, key) 100 } 101 return dedupedKeys 102 } 103 104 // AllKeys returns all the possible keys for this map. The keys for the map are 105 // potentially hidden and spread across multiple internal data structures and 106 // so this function conveniently packages them up. 107 func (m ChangeMap) AllKeys() []string { 108 keys := make(map[string]bool) 109 for before := range m.Before { 110 if _, ok := keys[before]; ok { 111 continue 112 } 113 keys[before] = true 114 } 115 for after := range m.After { 116 if _, ok := keys[after]; ok { 117 continue 118 } 119 keys[after] = true 120 } 121 for unknown := range m.Unknown { 122 if _, ok := keys[unknown]; ok { 123 continue 124 } 125 keys[unknown] = true 126 } 127 for sensitive := range m.AfterSensitive { 128 if _, ok := keys[sensitive]; ok { 129 continue 130 } 131 keys[sensitive] = true 132 } 133 for sensitive := range m.BeforeSensitive { 134 if _, ok := keys[sensitive]; ok { 135 continue 136 } 137 keys[sensitive] = true 138 } 139 140 var dedupedKeys []string 141 for key := range keys { 142 dedupedKeys = append(dedupedKeys, key) 143 } 144 return dedupedKeys 145 } 146 147 func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) { 148 if generic == nil { 149 return nil, false 150 } 151 152 if child, ok := generic[key]; ok { 153 return child, ok 154 } 155 return nil, false 156 } 157 158 func genericToMap(generic interface{}) map[string]interface{} { 159 if concrete, ok := generic.(map[string]interface{}); ok { 160 return concrete 161 } 162 return nil 163 }