github.com/sylr/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  }