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