github.com/jdextraze/terraform@v0.6.17-0.20160511153921-e33847c8a8af/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  }