github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/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 func (s *Set) GoString() string { 157 return fmt.Sprintf("*Set(%#v)", s.m) 158 } 159 160 func (s *Set) init() { 161 s.m = make(map[string]interface{}) 162 } 163 164 func (s *Set) add(item interface{}, computed bool) string { 165 s.once.Do(s.init) 166 167 code := s.hash(item) 168 if computed { 169 code = "~" + code 170 } 171 172 if _, ok := s.m[code]; !ok { 173 s.m[code] = item 174 } 175 176 return code 177 } 178 179 func (s *Set) hash(item interface{}) string { 180 code := s.F(item) 181 // Always return a nonnegative hashcode. 182 if code < 0 { 183 code = -code 184 } 185 return strconv.Itoa(code) 186 } 187 188 func (s *Set) remove(item interface{}) string { 189 s.once.Do(s.init) 190 191 code := s.hash(item) 192 delete(s.m, code) 193 194 return code 195 } 196 197 func (s *Set) index(item interface{}) int { 198 return sort.SearchStrings(s.listCode(), s.hash(item)) 199 } 200 201 func (s *Set) listCode() []string { 202 // Sort the hash codes so the order of the list is deterministic 203 keys := make([]string, 0, len(s.m)) 204 for k := range s.m { 205 keys = append(keys, k) 206 } 207 sort.Sort(sort.StringSlice(keys)) 208 return keys 209 }