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