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  }