| package structured |
| |
| import ( |
| "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" |
| ) |
| |
| // ChangeMap is a Change that represents a Map or an Object type, and has |
| // converted the relevant interfaces into maps for easier access. |
| type ChangeMap struct { |
| // Before contains the value before the proposed change. |
| Before map[string]interface{} |
| |
| // After contains the value after the proposed change. |
| After map[string]interface{} |
| |
| // Unknown contains the unknown status of any elements/attributes of this |
| // map/object. |
| Unknown map[string]interface{} |
| |
| // BeforeSensitive contains the before sensitive status of any |
| // elements/attributes of this map/object. |
| BeforeSensitive map[string]interface{} |
| |
| // AfterSensitive contains the after sensitive status of any |
| // elements/attributes of this map/object. |
| AfterSensitive map[string]interface{} |
| |
| // ReplacePaths matches the same attributes in Change exactly. |
| ReplacePaths attribute_path.Matcher |
| |
| // RelevantAttributes matches the same attributes in Change exactly. |
| RelevantAttributes attribute_path.Matcher |
| } |
| |
| // AsMap converts the Change into an object or map representation by converting |
| // the internal Before, After, Unknown, BeforeSensitive, and AfterSensitive |
| // data structures into generic maps. |
| func (change Change) AsMap() ChangeMap { |
| return ChangeMap{ |
| Before: genericToMap(change.Before), |
| After: genericToMap(change.After), |
| Unknown: genericToMap(change.Unknown), |
| BeforeSensitive: genericToMap(change.BeforeSensitive), |
| AfterSensitive: genericToMap(change.AfterSensitive), |
| ReplacePaths: change.ReplacePaths, |
| RelevantAttributes: change.RelevantAttributes, |
| } |
| } |
| |
| // GetChild safely packages up a Change object for the given child, handling |
| // all the cases where the data might be null or a static boolean. |
| func (m ChangeMap) GetChild(key string) Change { |
| before, beforeExplicit := getFromGenericMap(m.Before, key) |
| after, afterExplicit := getFromGenericMap(m.After, key) |
| unknown, _ := getFromGenericMap(m.Unknown, key) |
| beforeSensitive, _ := getFromGenericMap(m.BeforeSensitive, key) |
| afterSensitive, _ := getFromGenericMap(m.AfterSensitive, key) |
| |
| return Change{ |
| BeforeExplicit: beforeExplicit, |
| AfterExplicit: afterExplicit, |
| Before: before, |
| After: after, |
| Unknown: unknown, |
| BeforeSensitive: beforeSensitive, |
| AfterSensitive: afterSensitive, |
| ReplacePaths: m.ReplacePaths.GetChildWithKey(key), |
| RelevantAttributes: m.RelevantAttributes.GetChildWithKey(key), |
| } |
| } |
| |
| // ExplicitKeys returns the keys in the Before and After, as opposed to AllKeys |
| // which also includes keys from the additional meta structures (like the |
| // sensitive and unknown values). |
| // |
| // This function is useful for processing nested attributes and repeated blocks |
| // where the unknown and sensitive structs contain information about the actual |
| // attributes, while the before and after structs hold the actual nested values. |
| func (m ChangeMap) ExplicitKeys() []string { |
| keys := make(map[string]bool) |
| for before := range m.Before { |
| if _, ok := keys[before]; ok { |
| continue |
| } |
| keys[before] = true |
| } |
| for after := range m.After { |
| if _, ok := keys[after]; ok { |
| continue |
| } |
| keys[after] = true |
| } |
| |
| var dedupedKeys []string |
| for key := range keys { |
| dedupedKeys = append(dedupedKeys, key) |
| } |
| return dedupedKeys |
| } |
| |
| // AllKeys returns all the possible keys for this map. The keys for the map are |
| // potentially hidden and spread across multiple internal data structures and |
| // so this function conveniently packages them up. |
| func (m ChangeMap) AllKeys() []string { |
| keys := make(map[string]bool) |
| for before := range m.Before { |
| if _, ok := keys[before]; ok { |
| continue |
| } |
| keys[before] = true |
| } |
| for after := range m.After { |
| if _, ok := keys[after]; ok { |
| continue |
| } |
| keys[after] = true |
| } |
| for unknown := range m.Unknown { |
| if _, ok := keys[unknown]; ok { |
| continue |
| } |
| keys[unknown] = true |
| } |
| for sensitive := range m.AfterSensitive { |
| if _, ok := keys[sensitive]; ok { |
| continue |
| } |
| keys[sensitive] = true |
| } |
| for sensitive := range m.BeforeSensitive { |
| if _, ok := keys[sensitive]; ok { |
| continue |
| } |
| keys[sensitive] = true |
| } |
| |
| var dedupedKeys []string |
| for key := range keys { |
| dedupedKeys = append(dedupedKeys, key) |
| } |
| return dedupedKeys |
| } |
| |
| func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) { |
| if generic == nil { |
| return nil, false |
| } |
| |
| if child, ok := generic[key]; ok { |
| return child, ok |
| } |
| return nil, false |
| } |
| |
| func genericToMap(generic interface{}) map[string]interface{} { |
| if concrete, ok := generic.(map[string]interface{}); ok { |
| return concrete |
| } |
| return nil |
| } |