github.com/cmalfait/terraform@v0.11.12-beta1/helper/schema/set.go (about) 1 package schema 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "sort" 8 "strconv" 9 "sync" 10 11 "github.com/hashicorp/terraform/helper/hashcode" 12 ) 13 14 // HashString hashes strings. If you want a Set of strings, this is the 15 // SchemaSetFunc you want. 16 func HashString(v interface{}) int { 17 return hashcode.String(v.(string)) 18 } 19 20 // HashInt hashes integers. If you want a Set of integers, this is the 21 // SchemaSetFunc you want. 22 func HashInt(v interface{}) int { 23 return hashcode.String(strconv.Itoa(v.(int))) 24 } 25 26 // HashResource hashes complex structures that are described using 27 // a *Resource. This is the default set implementation used when a set's 28 // element type is a full resource. 29 func HashResource(resource *Resource) SchemaSetFunc { 30 return func(v interface{}) int { 31 var buf bytes.Buffer 32 SerializeResourceForHash(&buf, v, resource) 33 return hashcode.String(buf.String()) 34 } 35 } 36 37 // HashSchema hashes values that are described using a *Schema. This is the 38 // default set implementation used when a set's element type is a single 39 // schema. 40 func HashSchema(schema *Schema) SchemaSetFunc { 41 return func(v interface{}) int { 42 var buf bytes.Buffer 43 SerializeValueForHash(&buf, v, schema) 44 return hashcode.String(buf.String()) 45 } 46 } 47 48 // Set is a set data structure that is returned for elements of type 49 // TypeSet. 50 type Set struct { 51 F SchemaSetFunc 52 53 m map[string]interface{} 54 once sync.Once 55 } 56 57 // NewSet is a convenience method for creating a new set with the given 58 // items. 59 func NewSet(f SchemaSetFunc, items []interface{}) *Set { 60 s := &Set{F: f} 61 for _, i := range items { 62 s.Add(i) 63 } 64 65 return s 66 } 67 68 // CopySet returns a copy of another set. 69 func CopySet(otherSet *Set) *Set { 70 return NewSet(otherSet.F, otherSet.List()) 71 } 72 73 // Add adds an item to the set if it isn't already in the set. 74 func (s *Set) Add(item interface{}) { 75 s.add(item, false) 76 } 77 78 // Remove removes an item if it's already in the set. Idempotent. 79 func (s *Set) Remove(item interface{}) { 80 s.remove(item) 81 } 82 83 // Contains checks if the set has the given item. 84 func (s *Set) Contains(item interface{}) bool { 85 _, ok := s.m[s.hash(item)] 86 return ok 87 } 88 89 // Len returns the amount of items in the set. 90 func (s *Set) Len() int { 91 return len(s.m) 92 } 93 94 // List returns the elements of this set in slice format. 95 // 96 // The order of the returned elements is deterministic. Given the same 97 // set, the order of this will always be the same. 98 func (s *Set) List() []interface{} { 99 result := make([]interface{}, len(s.m)) 100 for i, k := range s.listCode() { 101 result[i] = s.m[k] 102 } 103 104 return result 105 } 106 107 // Difference performs a set difference of the two sets, returning 108 // a new third set that has only the elements unique to this set. 109 func (s *Set) Difference(other *Set) *Set { 110 result := &Set{F: s.F} 111 result.once.Do(result.init) 112 113 for k, v := range s.m { 114 if _, ok := other.m[k]; !ok { 115 result.m[k] = v 116 } 117 } 118 119 return result 120 } 121 122 // Intersection performs the set intersection of the two sets 123 // and returns a new third set. 124 func (s *Set) Intersection(other *Set) *Set { 125 result := &Set{F: s.F} 126 result.once.Do(result.init) 127 128 for k, v := range s.m { 129 if _, ok := other.m[k]; ok { 130 result.m[k] = v 131 } 132 } 133 134 return result 135 } 136 137 // Union performs the set union of the two sets and returns a new third 138 // set. 139 func (s *Set) Union(other *Set) *Set { 140 result := &Set{F: s.F} 141 result.once.Do(result.init) 142 143 for k, v := range s.m { 144 result.m[k] = v 145 } 146 for k, v := range other.m { 147 result.m[k] = v 148 } 149 150 return result 151 } 152 153 func (s *Set) Equal(raw interface{}) bool { 154 other, ok := raw.(*Set) 155 if !ok { 156 return false 157 } 158 159 return reflect.DeepEqual(s.m, other.m) 160 } 161 162 // HashEqual simply checks to the keys the top-level map to the keys in the 163 // other set's top-level map to see if they are equal. This obviously assumes 164 // you have a properly working hash function - use HashResource if in doubt. 165 func (s *Set) HashEqual(raw interface{}) bool { 166 other, ok := raw.(*Set) 167 if !ok { 168 return false 169 } 170 171 ks1 := make([]string, 0) 172 ks2 := make([]string, 0) 173 174 for k := range s.m { 175 ks1 = append(ks1, k) 176 } 177 for k := range other.m { 178 ks2 = append(ks2, k) 179 } 180 181 sort.Strings(ks1) 182 sort.Strings(ks2) 183 184 return reflect.DeepEqual(ks1, ks2) 185 } 186 187 func (s *Set) GoString() string { 188 return fmt.Sprintf("*Set(%#v)", s.m) 189 } 190 191 func (s *Set) init() { 192 s.m = make(map[string]interface{}) 193 } 194 195 func (s *Set) add(item interface{}, computed bool) string { 196 s.once.Do(s.init) 197 198 code := s.hash(item) 199 if computed { 200 code = "~" + code 201 } 202 203 if _, ok := s.m[code]; !ok { 204 s.m[code] = item 205 } 206 207 return code 208 } 209 210 func (s *Set) hash(item interface{}) string { 211 code := s.F(item) 212 // Always return a nonnegative hashcode. 213 if code < 0 { 214 code = -code 215 } 216 return strconv.Itoa(code) 217 } 218 219 func (s *Set) remove(item interface{}) string { 220 s.once.Do(s.init) 221 222 code := s.hash(item) 223 delete(s.m, code) 224 225 return code 226 } 227 228 func (s *Set) index(item interface{}) int { 229 return sort.SearchStrings(s.listCode(), s.hash(item)) 230 } 231 232 func (s *Set) listCode() []string { 233 // Sort the hash codes so the order of the list is deterministic 234 keys := make([]string, 0, len(s.m)) 235 for k := range s.m { 236 keys = append(keys, k) 237 } 238 sort.Sort(sort.StringSlice(keys)) 239 return keys 240 }