]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/flatmap/expand.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / flatmap / expand.go
1 package flatmap
2
3 import (
4 "fmt"
5 "sort"
6 "strconv"
7 "strings"
8
9 "github.com/hashicorp/hil"
10 )
11
12 // Expand takes a map and a key (prefix) and expands that value into
13 // a more complex structure. This is the reverse of the Flatten operation.
14 func Expand(m map[string]string, key string) interface{} {
15 // If the key is exactly a key in the map, just return it
16 if v, ok := m[key]; ok {
17 if v == "true" {
18 return true
19 } else if v == "false" {
20 return false
21 }
22
23 return v
24 }
25
26 // Check if the key is an array, and if so, expand the array
27 if v, ok := m[key+".#"]; ok {
28 // If the count of the key is unknown, then just put the unknown
29 // value in the value itself. This will be detected by Terraform
30 // core later.
31 if v == hil.UnknownValue {
32 return v
33 }
34
35 return expandArray(m, key)
36 }
37
38 // Check if this is a prefix in the map
39 prefix := key + "."
40 for k := range m {
41 if strings.HasPrefix(k, prefix) {
42 return expandMap(m, prefix)
43 }
44 }
45
46 return nil
47 }
48
49 func expandArray(m map[string]string, prefix string) []interface{} {
50 num, err := strconv.ParseInt(m[prefix+".#"], 0, 0)
51 if err != nil {
52 panic(err)
53 }
54
55 // If the number of elements in this array is 0, then return an
56 // empty slice as there is nothing to expand. Trying to expand it
57 // anyway could lead to crashes as any child maps, arrays or sets
58 // that no longer exist are still shown as empty with a count of 0.
59 if num == 0 {
60 return []interface{}{}
61 }
62
63 // NOTE: "num" is not necessarily accurate, e.g. if a user tampers
64 // with state, so the following code should not crash when given a
65 // number of items more or less than what's given in num. The
66 // num key is mainly just a hint that this is a list or set.
67
68 // The Schema "Set" type stores its values in an array format, but
69 // using numeric hash values instead of ordinal keys. Take the set
70 // of keys regardless of value, and expand them in numeric order.
71 // See GH-11042 for more details.
72 keySet := map[int]bool{}
73 computed := map[string]bool{}
74 for k := range m {
75 if !strings.HasPrefix(k, prefix+".") {
76 continue
77 }
78
79 key := k[len(prefix)+1:]
80 idx := strings.Index(key, ".")
81 if idx != -1 {
82 key = key[:idx]
83 }
84
85 // skip the count value
86 if key == "#" {
87 continue
88 }
89
90 // strip the computed flag if there is one
91 if strings.HasPrefix(key, "~") {
92 key = key[1:]
93 computed[key] = true
94 }
95
96 k, err := strconv.Atoi(key)
97 if err != nil {
98 panic(err)
99 }
100 keySet[int(k)] = true
101 }
102
103 keysList := make([]int, 0, num)
104 for key := range keySet {
105 keysList = append(keysList, key)
106 }
107 sort.Ints(keysList)
108
109 result := make([]interface{}, len(keysList))
110 for i, key := range keysList {
111 keyString := strconv.Itoa(key)
112 if computed[keyString] {
113 keyString = "~" + keyString
114 }
115 result[i] = Expand(m, fmt.Sprintf("%s.%s", prefix, keyString))
116 }
117
118 return result
119 }
120
121 func expandMap(m map[string]string, prefix string) map[string]interface{} {
122 // Submaps may not have a '%' key, so we can't count on this value being
123 // here. If we don't have a count, just proceed as if we have have a map.
124 if count, ok := m[prefix+"%"]; ok && count == "0" {
125 return map[string]interface{}{}
126 }
127
128 result := make(map[string]interface{})
129 for k := range m {
130 if !strings.HasPrefix(k, prefix) {
131 continue
132 }
133
134 key := k[len(prefix):]
135 idx := strings.Index(key, ".")
136 if idx != -1 {
137 key = key[:idx]
138 }
139 if _, ok := result[key]; ok {
140 continue
141 }
142
143 // skip the map count value
144 if key == "%" {
145 continue
146 }
147
148 result[key] = Expand(m, k[:len(prefix)+len(key)])
149 }
150
151 return result
152 }