blob: 0c8268ad6beba4d0c59fbba6057516aa22d48f12 [file] [log] [blame]
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
}