Ver código fonte

Merge pull request #45169 from corhere/libnet/generic-setmatrix

libn/i/setmatrix: make generic and constructorless
Cory Snider 2 anos atrás
pai
commit
77053da5c6

+ 2 - 2
libnetwork/controller.go

@@ -94,7 +94,7 @@ type Controller struct {
 	extKeyListener   net.Listener
 	watchCh          chan *Endpoint
 	unWatchCh        chan *Endpoint
-	svcRecords       map[string]svcInfo
+	svcRecords       map[string]*svcInfo
 	nmap             map[string]*netWatch
 	serviceBindings  map[serviceKey]*service
 	defOsSbox        osl.Sandbox
@@ -120,7 +120,7 @@ func New(cfgOptions ...config.Option) (*Controller, error) {
 		id:               stringid.GenerateRandomID(),
 		cfg:              config.New(cfgOptions...),
 		sandboxes:        sandboxTable{},
-		svcRecords:       make(map[string]svcInfo),
+		svcRecords:       make(map[string]*svcInfo),
 		serviceBindings:  make(map[serviceKey]*service),
 		agentInitDone:    make(chan struct{}),
 		networkLocker:    locker.New(),

+ 5 - 8
libnetwork/drivers/overlay/peerdb.go

@@ -60,8 +60,8 @@ func (p *peerEntryDB) UnMarshalDB() peerEntry {
 }
 
 type peerMap struct {
-	// set of peerEntry, note they have to be objects and not pointers to maintain the proper equality checks
-	mp *setmatrix.SetMatrix
+	// set of peerEntry, note the values have to be objects and not pointers to maintain the proper equality checks
+	mp setmatrix.SetMatrix[peerEntryDB]
 	sync.Mutex
 }
 
@@ -122,7 +122,7 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool
 	for _, pKeyStr := range pMap.mp.Keys() {
 		entryDBList, ok := pMap.mp.Get(pKeyStr)
 		if ok {
-			peerEntryDB := entryDBList[0].(peerEntryDB)
+			peerEntryDB := entryDBList[0]
 			mp[pKeyStr] = peerEntryDB.UnMarshalDB()
 		}
 	}
@@ -170,11 +170,8 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
 	d.peerDb.Lock()
 	pMap, ok := d.peerDb.mp[nid]
 	if !ok {
-		d.peerDb.mp[nid] = &peerMap{
-			mp: setmatrix.NewSetMatrix(),
-		}
-
-		pMap = d.peerDb.mp[nid]
+		pMap = &peerMap{}
+		d.peerDb.mp[nid] = pMap
 	}
 	d.peerDb.Unlock()
 

+ 17 - 23
libnetwork/internal/setmatrix/setmatrix.go

@@ -3,29 +3,21 @@ package setmatrix
 import (
 	"sync"
 
-	mapset "github.com/deckarep/golang-set"
+	mapset "github.com/deckarep/golang-set/v2"
 )
 
 // SetMatrix is a map of Sets.
-type SetMatrix struct {
-	matrix map[string]mapset.Set
+// The zero value is an empty set matrix ready to use.
+//
+// SetMatrix values are safe for concurrent use.
+type SetMatrix[T comparable] struct {
+	matrix map[string]mapset.Set[T]
 
 	mu sync.Mutex
 }
 
-// NewSetMatrix creates a new set matrix object.
-func NewSetMatrix() *SetMatrix {
-	s := &SetMatrix{}
-	s.init()
-	return s
-}
-
-func (s *SetMatrix) init() {
-	s.matrix = make(map[string]mapset.Set)
-}
-
 // Get returns the members of the set for a specific key as a slice.
-func (s *SetMatrix) Get(key string) ([]interface{}, bool) {
+func (s *SetMatrix[T]) Get(key string) ([]T, bool) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
@@ -36,7 +28,7 @@ func (s *SetMatrix) Get(key string) ([]interface{}, bool) {
 }
 
 // Contains is used to verify if an element is in a set for a specific key.
-func (s *SetMatrix) Contains(key string, value interface{}) (containsElement, setExists bool) {
+func (s *SetMatrix[T]) Contains(key string, value T) (containsElement, setExists bool) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
@@ -48,13 +40,15 @@ func (s *SetMatrix) Contains(key string, value interface{}) (containsElement, se
 
 // Insert inserts the value in the set of a key and returns whether the value is
 // inserted (was not already in the set) and the number of elements in the set.
-func (s *SetMatrix) Insert(key string, value interface{}) (insetrted bool, cardinality int) {
+func (s *SetMatrix[T]) Insert(key string, value T) (inserted bool, cardinality int) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
 	if !ok {
-		s.matrix[key] = mapset.NewSet()
-		s.matrix[key].Add(value)
+		if s.matrix == nil {
+			s.matrix = make(map[string]mapset.Set[T])
+		}
+		s.matrix[key] = mapset.NewThreadUnsafeSet(value)
 		return true, 1
 	}
 
@@ -62,7 +56,7 @@ func (s *SetMatrix) Insert(key string, value interface{}) (insetrted bool, cardi
 }
 
 // Remove removes the value in the set for a specific key.
-func (s *SetMatrix) Remove(key string, value interface{}) (removed bool, cardinality int) {
+func (s *SetMatrix[T]) Remove(key string, value T) (removed bool, cardinality int) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
@@ -83,7 +77,7 @@ func (s *SetMatrix) Remove(key string, value interface{}) (removed bool, cardina
 }
 
 // Cardinality returns the number of elements in the set for a key.
-func (s *SetMatrix) Cardinality(key string) (cardinality int, ok bool) {
+func (s *SetMatrix[T]) Cardinality(key string) (cardinality int, ok bool) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
@@ -96,7 +90,7 @@ func (s *SetMatrix) Cardinality(key string) (cardinality int, ok bool) {
 
 // String returns the string version of the set.
 // The empty string is returned if there is no set for key.
-func (s *SetMatrix) String(key string) (v string, ok bool) {
+func (s *SetMatrix[T]) String(key string) (v string, ok bool) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	set, ok := s.matrix[key]
@@ -107,7 +101,7 @@ func (s *SetMatrix) String(key string) (v string, ok bool) {
 }
 
 // Keys returns all the keys in the map.
-func (s *SetMatrix) Keys() []string {
+func (s *SetMatrix[T]) Keys() []string {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	keys := make([]string, 0, len(s.matrix))

+ 4 - 4
libnetwork/internal/setmatrix/setmatrix_test.go

@@ -9,7 +9,7 @@ import (
 )
 
 func TestSetSerialInsertDelete(t *testing.T) {
-	s := NewSetMatrix()
+	var s SetMatrix[string]
 
 	b, i := s.Insert("a", "1")
 	if !b || i != 1 {
@@ -135,7 +135,7 @@ func TestSetSerialInsertDelete(t *testing.T) {
 	}
 }
 
-func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix, key, value string) {
+func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix[string], key, value string) {
 	for {
 		select {
 		case <-ctx.Done():
@@ -158,14 +158,14 @@ func insertDeleteRotuine(ctx context.Context, endCh chan int, s *SetMatrix, key,
 }
 
 func TestSetParallelInsertDelete(t *testing.T) {
-	s := NewSetMatrix()
+	var s SetMatrix[string]
 	parallelRoutines := 6
 	endCh := make(chan int)
 	// Let the routines running and competing for 10s
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 	for i := 0; i < parallelRoutines; i++ {
-		go insertDeleteRotuine(ctx, endCh, s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i))
+		go insertDeleteRotuine(ctx, endCh, &s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i))
 	}
 	for parallelRoutines > 0 {
 		v := <-endCh

+ 2 - 6
libnetwork/libnetwork_internal_test.go

@@ -11,7 +11,6 @@ import (
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
-	"github.com/docker/docker/libnetwork/internal/setmatrix"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/netutils"
@@ -389,11 +388,8 @@ func TestSRVServiceQuery(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	sr := svcInfo{
-		svcMap:     setmatrix.NewSetMatrix(),
-		svcIPv6Map: setmatrix.NewSetMatrix(),
-		ipMap:      setmatrix.NewSetMatrix(),
-		service:    make(map[string][]servicePorts),
+	sr := &svcInfo{
+		service: make(map[string][]servicePorts),
 	}
 	// backing container for the service
 	cTarget := serviceTarget{

+ 22 - 32
libnetwork/network.go

@@ -105,9 +105,9 @@ type svcMapEntry struct {
 }
 
 type svcInfo struct {
-	svcMap     *setmatrix.SetMatrix
-	svcIPv6Map *setmatrix.SetMatrix
-	ipMap      *setmatrix.SetMatrix
+	svcMap     setmatrix.SetMatrix[svcMapEntry]
+	svcIPv6Map setmatrix.SetMatrix[svcMapEntry]
+	ipMap      setmatrix.SetMatrix[ipInfo]
 	service    map[string][]servicePorts
 }
 
@@ -1362,7 +1362,7 @@ func (n *network) updateSvcRecord(ep *Endpoint, localEps []*Endpoint, isAdd bool
 	}
 }
 
-func addIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
+func addIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) {
 	reverseIP := netutils.ReverseIP(ip.String())
 	ipMap.Insert(reverseIP, ipInfo{
 		name:      name,
@@ -1370,7 +1370,7 @@ func addIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP)
 	})
 }
 
-func delIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
+func delIPToName(ipMap *setmatrix.SetMatrix[ipInfo], name, serviceID string, ip net.IP) {
 	reverseIP := netutils.ReverseIP(ip.String())
 	ipMap.Remove(reverseIP, ipInfo{
 		name:      name,
@@ -1378,7 +1378,7 @@ func delIPToName(ipMap *setmatrix.SetMatrix, name, serviceID string, ip net.IP)
 	})
 }
 
-func addNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
+func addNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) {
 	// Since DNS name resolution is case-insensitive, Use the lower-case form
 	// of the name as the key into svcMap
 	lowerCaseName := strings.ToLower(name)
@@ -1388,7 +1388,7 @@ func addNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.I
 	})
 }
 
-func delNameToIP(svcMap *setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
+func delNameToIP(svcMap *setmatrix.SetMatrix[svcMapEntry], name, serviceID string, epIP net.IP) {
 	lowerCaseName := strings.ToLower(name)
 	svcMap.Remove(lowerCaseName, svcMapEntry{
 		ip:        epIP.String(),
@@ -1411,24 +1411,20 @@ func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP
 
 	sr, ok := c.svcRecords[networkID]
 	if !ok {
-		sr = svcInfo{
-			svcMap:     setmatrix.NewSetMatrix(),
-			svcIPv6Map: setmatrix.NewSetMatrix(),
-			ipMap:      setmatrix.NewSetMatrix(),
-		}
+		sr = &svcInfo{}
 		c.svcRecords[networkID] = sr
 	}
 
 	if ipMapUpdate {
-		addIPToName(sr.ipMap, name, serviceID, epIP)
+		addIPToName(&sr.ipMap, name, serviceID, epIP)
 		if epIPv6 != nil {
-			addIPToName(sr.ipMap, name, serviceID, epIPv6)
+			addIPToName(&sr.ipMap, name, serviceID, epIPv6)
 		}
 	}
 
-	addNameToIP(sr.svcMap, name, serviceID, epIP)
+	addNameToIP(&sr.svcMap, name, serviceID, epIP)
 	if epIPv6 != nil {
-		addNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6)
+		addNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
 	}
 }
 
@@ -1451,17 +1447,17 @@ func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epI
 	}
 
 	if ipMapUpdate {
-		delIPToName(sr.ipMap, name, serviceID, epIP)
+		delIPToName(&sr.ipMap, name, serviceID, epIP)
 
 		if epIPv6 != nil {
-			delIPToName(sr.ipMap, name, serviceID, epIPv6)
+			delIPToName(&sr.ipMap, name, serviceID, epIPv6)
 		}
 	}
 
-	delNameToIP(sr.svcMap, name, serviceID, epIP)
+	delNameToIP(&sr.svcMap, name, serviceID, epIP)
 
 	if epIPv6 != nil {
-		delNameToIP(sr.svcIPv6Map, name, serviceID, epIPv6)
+		delNameToIP(&sr.svcIPv6Map, name, serviceID, epIPv6)
 	}
 }
 
@@ -1480,7 +1476,7 @@ func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record {
 	n.ctrlr.mu.Lock()
 	defer n.ctrlr.mu.Unlock()
 	sr, ok := n.ctrlr.svcRecords[n.id]
-	if !ok || sr.svcMap == nil {
+	if !ok {
 		return nil
 	}
 
@@ -1503,7 +1499,7 @@ func (n *network) getSvcRecords(ep *Endpoint) []etchosts.Record {
 
 		recs = append(recs, etchosts.Record{
 			Hosts: k,
-			IP:    mapEntryList[0].(svcMapEntry).ip,
+			IP:    mapEntryList[0].ip,
 		})
 	}
 
@@ -2005,9 +2001,9 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
 		noDup := make(map[string]bool)
 		var ipLocal []net.IP
 		for _, ip := range ipSet {
-			if _, dup := noDup[ip.(svcMapEntry).ip]; !dup {
-				noDup[ip.(svcMapEntry).ip] = true
-				ipLocal = append(ipLocal, net.ParseIP(ip.(svcMapEntry).ip))
+			if _, dup := noDup[ip.ip]; !dup {
+				noDup[ip.ip] = true
+				ipLocal = append(ipLocal, net.ParseIP(ip.ip))
 			}
 		}
 		return ipLocal, ok
@@ -2058,13 +2054,7 @@ func (n *network) ResolveIP(ip string) string {
 	// network db notifications)
 	// In such cases the resolution will be based on the first element of the set, and can vary
 	// during the system stabilitation
-	elem, ok := elemSet[0].(ipInfo)
-	if !ok {
-		setStr, b := sr.ipMap.String(ip)
-		logrus.Errorf("expected set of ipInfo type for key %s set:%t %s", ip, b, setStr)
-		return ""
-	}
-
+	elem := elemSet[0]
 	if elem.extResolver {
 		return ""
 	}

+ 1 - 1
libnetwork/service.go

@@ -54,7 +54,7 @@ type service struct {
 	// associated with it. At stable state the endpoint ID expected is 1
 	// but during transition and service change it is possible to have
 	// temporary more than 1
-	ipToEndpoint *setmatrix.SetMatrix
+	ipToEndpoint setmatrix.SetMatrix[string]
 
 	deleted bool
 

+ 1 - 3
libnetwork/service_common.go

@@ -6,7 +6,6 @@ package libnetwork
 import (
 	"net"
 
-	"github.com/docker/docker/libnetwork/internal/setmatrix"
 	"github.com/sirupsen/logrus"
 )
 
@@ -144,7 +143,6 @@ func newService(name string, id string, ingressPorts []*PortConfig, serviceAlias
 		ingressPorts:  ingressPorts,
 		loadBalancers: make(map[string]*loadBalancer),
 		aliases:       serviceAliases,
-		ipToEndpoint:  setmatrix.NewSetMatrix(),
 	}
 }
 
@@ -174,7 +172,7 @@ func (c *Controller) cleanupServiceDiscovery(cleanupNID string) {
 	defer c.mu.Unlock()
 	if cleanupNID == "" {
 		logrus.Debugf("cleanupServiceDiscovery for all networks")
-		c.svcRecords = make(map[string]svcInfo)
+		c.svcRecords = make(map[string]*svcInfo)
 		return
 	}
 	logrus.Debugf("cleanupServiceDiscovery for network:%s", cleanupNID)

+ 1 - 1
vendor.mod

@@ -31,7 +31,7 @@ require (
 	github.com/containerd/typeurl/v2 v2.1.0
 	github.com/coreos/go-systemd/v22 v22.5.0
 	github.com/creack/pty v1.1.11
-	github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9
+	github.com/deckarep/golang-set/v2 v2.3.0
 	github.com/docker/distribution v2.8.1+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c

+ 2 - 2
vendor.sum

@@ -484,8 +484,8 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 h1:YpTz1+8tEHbybtxtMJNkV3U3GBAA05EakMRTR3dXkis=
-github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
+github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
 github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
 github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=

+ 0 - 9
vendor/github.com/deckarep/golang-set/.travis.yml

@@ -1,9 +0,0 @@
-language: go
-
-go:
-    - 1.2
-
-script:
-    - go test ./...
-    #- go test -race ./...
-

+ 0 - 94
vendor/github.com/deckarep/golang-set/README.md

@@ -1,94 +0,0 @@
-[![Build Status](https://travis-ci.org/deckarep/golang-set.png?branch=master)](https://travis-ci.org/deckarep/golang-set)
-[![GoDoc](https://godoc.org/github.com/deckarep/golang-set?status.png)](http://godoc.org/github.com/deckarep/golang-set)
-
-## golang-set
-
-
-The missing set collection for the Go language.  Until Go has sets built-in...use this.
-
-Coming from Python one of the things I miss is the superbly wonderful set collection.  This is my attempt to mimic the primary features of the set from Python.
-You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library.  To those I say simply ignore this repository
-and carry-on and to the rest that find this useful please contribute in helping me make it better by:
-
-* Helping to make more idiomatic improvements to the code.
-* Helping to increase the performance of it. ~~(So far, no attempt has been made, but since it uses a map internally, I expect it to be mostly performant.)~~
-* Helping to make the unit-tests more robust and kick-ass.
-* Helping to fill in the [documentation.](http://godoc.org/github.com/deckarep/golang-set)
-* Simply offering feedback and suggestions.  (Positive, constructive feedback is appreciated.)
-
-I have to give some credit for helping seed the idea with this post on [stackoverflow.](http://programmers.stackexchange.com/questions/177428/sets-data-structure-in-golang)
-
-*Update* - as of 3/9/2014, you can use a compile-time generic version of this package in the [gen](http://clipperhouse.github.io/gen/) framework.  This framework allows you to use the golang-set in a completely generic and type-safe way by allowing you to generate a supporting .go file based on your custom types.
-
-## Features (as of 9/22/2014)
-
-* a CartesionProduct() method has been added with unit-tests: [Read more about the cartesion product](http://en.wikipedia.org/wiki/Cartesian_product)
-
-## Features (as of 9/15/2014)
-
-* a PowerSet() method has been added with unit-tests: [Read more about the Power set](http://en.wikipedia.org/wiki/Power_set)
-
-## Features (as of 4/22/2014)
-
-* One common interface to both implementations
-* Two set implementations to choose from
-  * a thread-safe implementation designed for concurrent use
-  * a non-thread-safe implementation designed for performance
-* 75 benchmarks for both implementations
-* 35 unit tests for both implementations
-* 14 concurrent tests for the thread-safe implementation
-
-
-
-Please see the unit test file for additional usage examples.  The Python set documentation will also do a better job than I can of explaining how a set typically [works.](http://docs.python.org/2/library/sets.html)    Please keep in mind
-however that the Python set is a built-in type and supports additional features and syntax that make it awesome.
-
-## Examples but not exhaustive:
-
-```go
-requiredClasses := mapset.NewSet()
-requiredClasses.Add("Cooking")
-requiredClasses.Add("English")
-requiredClasses.Add("Math")
-requiredClasses.Add("Biology")
-
-scienceSlice := []interface{}{"Biology", "Chemistry"}
-scienceClasses := mapset.NewSetFromSlice(scienceSlice)
-
-electiveClasses := mapset.NewSet()
-electiveClasses.Add("Welding")
-electiveClasses.Add("Music")
-electiveClasses.Add("Automotive")
-
-bonusClasses := mapset.NewSet()
-bonusClasses.Add("Go Programming")
-bonusClasses.Add("Python Programming")
-
-//Show me all the available classes I can take
-allClasses := requiredClasses.Union(scienceClasses).Union(electiveClasses).Union(bonusClasses)
-fmt.Println(allClasses) //Set{Cooking, English, Math, Chemistry, Welding, Biology, Music, Automotive, Go Programming, Python Programming}
-
-
-//Is cooking considered a science class?
-fmt.Println(scienceClasses.Contains("Cooking")) //false
-
-//Show me all classes that are not science classes, since I hate science.
-fmt.Println(allClasses.Difference(scienceClasses)) //Set{Music, Automotive, Go Programming, Python Programming, Cooking, English, Math, Welding}
-
-//Which science classes are also required classes?
-fmt.Println(scienceClasses.Intersect(requiredClasses)) //Set{Biology}
-
-//How many bonus classes do you offer?
-fmt.Println(bonusClasses.Cardinality()) //2
-
-//Do you have the following classes? Welding, Automotive and English?
-fmt.Println(allClasses.IsSuperset(mapset.NewSetFromSlice([]interface{}{"Welding", "Automotive", "English"}))) //true
-```
-
-Thanks!
-
--Ralph
-
-[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/deckarep/golang-set/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
-
-[![Analytics](https://ga-beacon.appspot.com/UA-42584447-2/deckarep/golang-set)](https://github.com/igrigorik/ga-beacon)

+ 0 - 168
vendor/github.com/deckarep/golang-set/set.go

@@ -1,168 +0,0 @@
-/*
-Open Source Initiative OSI - The MIT License (MIT):Licensing
-
-The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-// Package mapset implements a simple and generic set collection.
-// Items stored within it are unordered and unique. It supports
-// typical set operations: membership testing, intersection, union,
-// difference, symmetric difference and cloning.
-//
-// Package mapset provides two implementations. The default
-// implementation is safe for concurrent access. There is a non-threadsafe
-// implementation which is slightly more performant.
-package mapset
-
-type Set interface {
-	// Adds an element to the set. Returns whether
-	// the item was added.
-	Add(i interface{}) bool
-
-	// Returns the number of elements in the set.
-	Cardinality() int
-
-	// Removes all elements from the set, leaving
-	// the emtpy set.
-	Clear()
-
-	// Returns a clone of the set using the same
-	// implementation, duplicating all keys.
-	Clone() Set
-
-	// Returns whether the given items
-	// are all in the set.
-	Contains(i ...interface{}) bool
-
-	// Returns the difference between this set
-	// and other. The returned set will contain
-	// all elements of this set that are not also
-	// elements of other.
-	//
-	// Note that the argument to Difference
-	// must be of the same type as the receiver
-	// of the method. Otherwise, Difference will
-	// panic.
-	Difference(other Set) Set
-
-	// Determines if two sets are equal to each
-	// other. If they have the same cardinality
-	// and contain the same elements, they are
-	// considered equal. The order in which
-	// the elements were added is irrelevant.
-	//
-	// Note that the argument to Equal must be
-	// of the same type as the receiver of the
-	// method. Otherwise, Equal will panic.
-	Equal(other Set) bool
-
-	// Returns a new set containing only the elements
-	// that exist only in both sets.
-	//
-	// Note that the argument to Intersect
-	// must be of the same type as the receiver
-	// of the method. Otherwise, Intersect will
-	// panic.
-	Intersect(other Set) Set
-
-	// Determines if every element in the other set
-	// is in this set.
-	//
-	// Note that the argument to IsSubset
-	// must be of the same type as the receiver
-	// of the method. Otherwise, IsSubset will
-	// panic.
-	IsSubset(other Set) bool
-
-	// Determines if every element in this set is in
-	// the other set.
-	//
-	// Note that the argument to IsSuperset
-	// must be of the same type as the receiver
-	// of the method. Otherwise, IsSuperset will
-	// panic.
-	IsSuperset(other Set) bool
-
-	// Returns a channel of elements that you can
-	// range over.
-	Iter() <-chan interface{}
-
-	// Remove a single element from the set.
-	Remove(i interface{})
-
-	// Provides a convenient string representation
-	// of the current state of the set.
-	String() string
-
-	// Returns a new set with all elements which are
-	// in either this set or the other set but not in both.
-	//
-	// Note that the argument to SymmetricDifference
-	// must be of the same type as the receiver
-	// of the method. Otherwise, SymmetricDifference
-	// will panic.
-	SymmetricDifference(other Set) Set
-
-	// Returns a new set with all elements in both sets.
-	//
-	// Note that the argument to Union must be of the
-	// same type as the receiver of the method.
-	// Otherwise, IsSuperset will panic.
-	Union(other Set) Set
-
-	// Returns all subsets of a given set (Power Set).
-	PowerSet() Set
-
-	// Returns the Cartesian Product of two sets.
-	CartesianProduct(other Set) Set
-
-	// Returns the members of the set as a slice.
-	ToSlice() []interface{}
-}
-
-// Creates and returns a reference to an empty set.
-func NewSet() Set {
-	set := newThreadSafeSet()
-	return &set
-}
-
-// Creates and returns a reference to a set from an existing slice
-func NewSetFromSlice(s []interface{}) Set {
-	a := NewSet()
-	for _, item := range s {
-		a.Add(item)
-	}
-	return a
-}
-
-func NewThreadUnsafeSet() Set {
-	set := newThreadUnsafeSet()
-	return &set
-}
-
-func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
-	a := NewThreadUnsafeSet()
-	for _, item := range s {
-		a.Add(item)
-	}
-	return a
-}

+ 0 - 204
vendor/github.com/deckarep/golang-set/threadsafe.go

@@ -1,204 +0,0 @@
-/*
-Open Source Initiative OSI - The MIT License (MIT):Licensing
-
-The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-package mapset
-
-import "sync"
-
-type threadSafeSet struct {
-	s threadUnsafeSet
-	sync.RWMutex
-}
-
-func newThreadSafeSet() threadSafeSet {
-	return threadSafeSet{s: newThreadUnsafeSet()}
-}
-
-func (set *threadSafeSet) Add(i interface{}) bool {
-	set.Lock()
-	ret := set.s.Add(i)
-	set.Unlock()
-	return ret
-}
-
-func (set *threadSafeSet) Contains(i ...interface{}) bool {
-	set.RLock()
-	ret := set.s.Contains(i...)
-	set.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) IsSubset(other Set) bool {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	ret := set.s.IsSubset(&o.s)
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) IsSuperset(other Set) bool {
-	return other.IsSubset(set)
-}
-
-func (set *threadSafeSet) Union(other Set) Set {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	unsafeUnion := set.s.Union(&o.s).(*threadUnsafeSet)
-	ret := &threadSafeSet{s: *unsafeUnion}
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) Intersect(other Set) Set {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	unsafeIntersection := set.s.Intersect(&o.s).(*threadUnsafeSet)
-	ret := &threadSafeSet{s: *unsafeIntersection}
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) Difference(other Set) Set {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	unsafeDifference := set.s.Difference(&o.s).(*threadUnsafeSet)
-	ret := &threadSafeSet{s: *unsafeDifference}
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) SymmetricDifference(other Set) Set {
-	o := other.(*threadSafeSet)
-
-	unsafeDifference := set.s.SymmetricDifference(&o.s).(*threadUnsafeSet)
-	return &threadSafeSet{s: *unsafeDifference}
-}
-
-func (set *threadSafeSet) Clear() {
-	set.Lock()
-	set.s = newThreadUnsafeSet()
-	set.Unlock()
-}
-
-func (set *threadSafeSet) Remove(i interface{}) {
-	set.Lock()
-	delete(set.s, i)
-	set.Unlock()
-}
-
-func (set *threadSafeSet) Cardinality() int {
-	set.RLock()
-	defer set.RUnlock()
-	return len(set.s)
-}
-
-func (set *threadSafeSet) Iter() <-chan interface{} {
-	ch := make(chan interface{})
-	go func() {
-		set.RLock()
-
-		for elem := range set.s {
-			ch <- elem
-		}
-		close(ch)
-		set.RUnlock()
-	}()
-
-	return ch
-}
-
-func (set *threadSafeSet) Equal(other Set) bool {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	ret := set.s.Equal(&o.s)
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) Clone() Set {
-	set.RLock()
-
-	unsafeClone := set.s.Clone().(*threadUnsafeSet)
-	ret := &threadSafeSet{s: *unsafeClone}
-	set.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) String() string {
-	set.RLock()
-	ret := set.s.String()
-	set.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) PowerSet() Set {
-	set.RLock()
-	ret := set.s.PowerSet()
-	set.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) CartesianProduct(other Set) Set {
-	o := other.(*threadSafeSet)
-
-	set.RLock()
-	o.RLock()
-
-	unsafeCartProduct := set.s.CartesianProduct(&o.s).(*threadUnsafeSet)
-	ret := &threadSafeSet{s: *unsafeCartProduct}
-	set.RUnlock()
-	o.RUnlock()
-	return ret
-}
-
-func (set *threadSafeSet) ToSlice() []interface{} {
-	set.RLock()
-	keys := make([]interface{}, 0, set.Cardinality())
-	for elem := range set.s {
-		keys = append(keys, elem)
-	}
-	set.RUnlock()
-	return keys
-}

+ 0 - 246
vendor/github.com/deckarep/golang-set/threadunsafe.go

@@ -1,246 +0,0 @@
-/*
-Open Source Initiative OSI - The MIT License (MIT):Licensing
-
-The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-package mapset
-
-import (
-	"fmt"
-	"reflect"
-	"strings"
-)
-
-type threadUnsafeSet map[interface{}]struct{}
-
-type orderedPair struct {
-	first  interface{}
-	second interface{}
-}
-
-func newThreadUnsafeSet() threadUnsafeSet {
-	return make(threadUnsafeSet)
-}
-
-func (pair *orderedPair) Equal(other orderedPair) bool {
-	if pair.first == other.first &&
-		pair.second == other.second {
-		return true
-	}
-
-	return false
-}
-
-func (set *threadUnsafeSet) Add(i interface{}) bool {
-	_, found := (*set)[i]
-	(*set)[i] = struct{}{}
-	return !found //False if it existed already
-}
-
-func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
-	for _, val := range i {
-		if _, ok := (*set)[val]; !ok {
-			return false
-		}
-	}
-	return true
-}
-
-func (set *threadUnsafeSet) IsSubset(other Set) bool {
-	_ = other.(*threadUnsafeSet)
-	for elem := range *set {
-		if !other.Contains(elem) {
-			return false
-		}
-	}
-	return true
-}
-
-func (set *threadUnsafeSet) IsSuperset(other Set) bool {
-	return other.IsSubset(set)
-}
-
-func (set *threadUnsafeSet) Union(other Set) Set {
-	o := other.(*threadUnsafeSet)
-
-	unionedSet := newThreadUnsafeSet()
-
-	for elem := range *set {
-		unionedSet.Add(elem)
-	}
-	for elem := range *o {
-		unionedSet.Add(elem)
-	}
-	return &unionedSet
-}
-
-func (set *threadUnsafeSet) Intersect(other Set) Set {
-	o := other.(*threadUnsafeSet)
-
-	intersection := newThreadUnsafeSet()
-	// loop over smaller set
-	if set.Cardinality() < other.Cardinality() {
-		for elem := range *set {
-			if other.Contains(elem) {
-				intersection.Add(elem)
-			}
-		}
-	} else {
-		for elem := range *o {
-			if set.Contains(elem) {
-				intersection.Add(elem)
-			}
-		}
-	}
-	return &intersection
-}
-
-func (set *threadUnsafeSet) Difference(other Set) Set {
-	_ = other.(*threadUnsafeSet)
-
-	difference := newThreadUnsafeSet()
-	for elem := range *set {
-		if !other.Contains(elem) {
-			difference.Add(elem)
-		}
-	}
-	return &difference
-}
-
-func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
-	_ = other.(*threadUnsafeSet)
-
-	aDiff := set.Difference(other)
-	bDiff := other.Difference(set)
-	return aDiff.Union(bDiff)
-}
-
-func (set *threadUnsafeSet) Clear() {
-	*set = newThreadUnsafeSet()
-}
-
-func (set *threadUnsafeSet) Remove(i interface{}) {
-	delete(*set, i)
-}
-
-func (set *threadUnsafeSet) Cardinality() int {
-	return len(*set)
-}
-
-func (set *threadUnsafeSet) Iter() <-chan interface{} {
-	ch := make(chan interface{})
-	go func() {
-		for elem := range *set {
-			ch <- elem
-		}
-		close(ch)
-	}()
-
-	return ch
-}
-
-func (set *threadUnsafeSet) Equal(other Set) bool {
-	_ = other.(*threadUnsafeSet)
-
-	if set.Cardinality() != other.Cardinality() {
-		return false
-	}
-	for elem := range *set {
-		if !other.Contains(elem) {
-			return false
-		}
-	}
-	return true
-}
-
-func (set *threadUnsafeSet) Clone() Set {
-	clonedSet := newThreadUnsafeSet()
-	for elem := range *set {
-		clonedSet.Add(elem)
-	}
-	return &clonedSet
-}
-
-func (set *threadUnsafeSet) String() string {
-	items := make([]string, 0, len(*set))
-
-	for elem := range *set {
-		items = append(items, fmt.Sprintf("%v", elem))
-	}
-	return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
-}
-
-func (pair orderedPair) String() string {
-	return fmt.Sprintf("(%v, %v)", pair.first, pair.second)
-}
-
-func (set *threadUnsafeSet) PowerSet() Set {
-	powSet := NewThreadUnsafeSet()
-	nullset := newThreadUnsafeSet()
-	powSet.Add(&nullset)
-
-	for es := range *set {
-		u := newThreadUnsafeSet()
-		j := powSet.Iter()
-		for er := range j {
-			p := newThreadUnsafeSet()
-			if reflect.TypeOf(er).Name() == "" {
-				k := er.(*threadUnsafeSet)
-				for ek := range *(k) {
-					p.Add(ek)
-				}
-			} else {
-				p.Add(er)
-			}
-			p.Add(es)
-			u.Add(&p)
-		}
-
-		powSet = powSet.Union(&u)
-	}
-
-	return powSet
-}
-
-func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
-	o := other.(*threadUnsafeSet)
-	cartProduct := NewThreadUnsafeSet()
-
-	for i := range *set {
-		for j := range *o {
-			elem := orderedPair{first: i, second: j}
-			cartProduct.Add(elem)
-		}
-	}
-
-	return cartProduct
-}
-
-func (set *threadUnsafeSet) ToSlice() []interface{} {
-	keys := make([]interface{}, 0, set.Cardinality())
-	for elem := range *set {
-		keys = append(keys, elem)
-	}
-
-	return keys
-}

+ 0 - 0
vendor/github.com/deckarep/golang-set/.gitignore → vendor/github.com/deckarep/golang-set/v2/.gitignore


+ 1 - 1
vendor/github.com/deckarep/golang-set/LICENSE → vendor/github.com/deckarep/golang-set/v2/LICENSE

@@ -1,7 +1,7 @@
 Open Source Initiative OSI - The MIT License (MIT):Licensing
 
 The MIT License (MIT)
-Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in

+ 173 - 0
vendor/github.com/deckarep/golang-set/v2/README.md

@@ -0,0 +1,173 @@
+![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg)
+[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2)
+[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2)
+
+# golang-set
+
+The missing `generic` set collection for the Go language.  Until Go has sets built-in...use this.
+
+## Update 3/5/2023
+* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`.
+* supports `new generic` syntax
+* Go `1.18.0` or higher
+* Workflow tested on Go `1.20`
+
+![With Generics](new_improved.jpeg)
+
+Coming from Python one of the things I miss is the superbly wonderful set collection.  This is my attempt to mimic the primary features of the set collection from Python.
+You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library.  To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs.
+
+## Features
+
+* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher)
+* One common *interface* to both implementations
+  * a **non threadsafe** implementation favoring *performance*
+  * a **threadsafe** implementation favoring *concurrent* use
+* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set).
+* Exhaustive unit-test and benchmark suite
+
+## Trusted by
+
+This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package.
+
+* Notable projects/companies using this package
+  * Ethereum
+  * Docker
+  * 1Password
+  * Hashicorp
+
+## Star History
+
+[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date)
+
+
+## Usage
+
+The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object.
+
+What is considered comparable in Go? 
+* `Booleans`, `integers`, `strings`, `floats` or basically primitive types.
+* `Pointers`
+* `Arrays`
+* `Structs` if *all of their fields* are also comparable independently
+
+Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection.
+
+```go
+// Syntax example, doesn't compile.
+mySet := mapset.NewSet[T]() // where T is some concrete comparable type.
+
+// Therefore this code creates an int set
+mySet := mapset.NewSet[int]()
+
+// Or perhaps you want a string set
+mySet := mapset.NewSet[string]()
+
+type myStruct {
+  name string
+  age uint8
+}
+
+// Alternatively a set of structs
+mySet := mapset.NewSet[myStruct]()
+
+// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety.
+mySet := mapset.NewSet[any]()
+```
+
+## Comprehensive Example
+
+```go
+package main
+
+import (
+  "fmt"
+  mapset "github.com/deckarep/golang-set/v2"
+)
+
+func main() {
+  // Create a string-based set of required classes.
+  required := mapset.NewSet[string]()
+  required.Add("cooking")
+  required.Add("english")
+  required.Add("math")
+  required.Add("biology")
+
+  // Create a string-based set of science classes.
+  sciences := mapset.NewSet[string]()
+  sciences.Add("biology")
+  sciences.Add("chemistry")
+  
+  // Create a string-based set of electives.
+  electives := mapset.NewSet[string]()
+  electives.Add("welding")
+  electives.Add("music")
+  electives.Add("automotive")
+
+  // Create a string-based set of bonus programming classes.
+  bonus := mapset.NewSet[string]()
+  bonus.Add("beginner go")
+  bonus.Add("python for dummies")
+}
+```
+
+Create a set of all unique classes.
+Sets will *automatically* deduplicate the same data.
+
+```go
+  all := required
+    .Union(sciences)
+    .Union(electives)
+    .Union(bonus)
+  
+  fmt.Println(all)
+```
+
+Output:
+```sh
+Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies}
+```
+
+Is cooking considered a science class?
+```go
+result := sciences.Contains("cooking")
+fmt.Println(result)
+```
+
+Output:
+```false
+false
+```
+
+Show me all classes that are not science classes, since I don't enjoy science.
+```go
+notScience := all.Difference(sciences)
+fmt.Println(notScience)
+```
+
+```sh
+Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding }
+```
+
+Which science classes are also required classes?
+```go
+reqScience := sciences.Intersect(required)
+```
+
+Output:
+```sh
+Set{biology}
+```
+
+How many bonus classes do you offer?
+```go
+fmt.Println(bonus.Cardinality())
+```
+Output:
+```sh
+2
+```
+
+Thanks for visiting!
+
+-deckarep

+ 58 - 0
vendor/github.com/deckarep/golang-set/v2/iterator.go

@@ -0,0 +1,58 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package mapset
+
+// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's
+// elements.
+type Iterator[T comparable] struct {
+	C    <-chan T
+	stop chan struct{}
+}
+
+// Stop stops the Iterator, no further elements will be received on C, C will be closed.
+func (i *Iterator[T]) Stop() {
+	// Allows for Stop() to be called multiple times
+	// (close() panics when called on already closed channel)
+	defer func() {
+		recover()
+	}()
+
+	close(i.stop)
+
+	// Exhaust any remaining elements.
+	for range i.C {
+	}
+}
+
+// newIterator returns a new Iterator instance together with its item and stop channels.
+func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) {
+	itemChan := make(chan T)
+	stopChan := make(chan struct{})
+	return &Iterator[T]{
+		C:    itemChan,
+		stop: stopChan,
+	}, itemChan, stopChan
+}

BIN
vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg


+ 241 - 0
vendor/github.com/deckarep/golang-set/v2/set.go

@@ -0,0 +1,241 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+// Package mapset implements a simple and  set collection.
+// Items stored within it are unordered and unique. It supports
+// typical set operations: membership testing, intersection, union,
+// difference, symmetric difference and cloning.
+//
+// Package mapset provides two implementations of the Set
+// interface. The default implementation is safe for concurrent
+// access, but a non-thread-safe implementation is also provided for
+// programs that can benefit from the slight speed improvement and
+// that can enforce mutual exclusion through other means.
+package mapset
+
+// Set is the primary interface provided by the mapset package.  It
+// represents an unordered set of data and a large number of
+// operations that can be applied to that set.
+type Set[T comparable] interface {
+	// Add adds an element to the set. Returns whether
+	// the item was added.
+	Add(val T) bool
+
+	// Append multiple elements to the set. Returns
+	// the number of elements added.
+	Append(val ...T) int
+
+	// Cardinality returns the number of elements in the set.
+	Cardinality() int
+
+	// Clear removes all elements from the set, leaving
+	// the empty set.
+	Clear()
+
+	// Clone returns a clone of the set using the same
+	// implementation, duplicating all keys.
+	Clone() Set[T]
+
+	// Contains returns whether the given items
+	// are all in the set.
+	Contains(val ...T) bool
+
+	// Difference returns the difference between this set
+	// and other. The returned set will contain
+	// all elements of this set that are not also
+	// elements of other.
+	//
+	// Note that the argument to Difference
+	// must be of the same type as the receiver
+	// of the method. Otherwise, Difference will
+	// panic.
+	Difference(other Set[T]) Set[T]
+
+	// Equal determines if two sets are equal to each
+	// other. If they have the same cardinality
+	// and contain the same elements, they are
+	// considered equal. The order in which
+	// the elements were added is irrelevant.
+	//
+	// Note that the argument to Equal must be
+	// of the same type as the receiver of the
+	// method. Otherwise, Equal will panic.
+	Equal(other Set[T]) bool
+
+	// Intersect returns a new set containing only the elements
+	// that exist only in both sets.
+	//
+	// Note that the argument to Intersect
+	// must be of the same type as the receiver
+	// of the method. Otherwise, Intersect will
+	// panic.
+	Intersect(other Set[T]) Set[T]
+
+	// IsProperSubset determines if every element in this set is in
+	// the other set but the two sets are not equal.
+	//
+	// Note that the argument to IsProperSubset
+	// must be of the same type as the receiver
+	// of the method. Otherwise, IsProperSubset
+	// will panic.
+	IsProperSubset(other Set[T]) bool
+
+	// IsProperSuperset determines if every element in the other set
+	// is in this set but the two sets are not
+	// equal.
+	//
+	// Note that the argument to IsSuperset
+	// must be of the same type as the receiver
+	// of the method. Otherwise, IsSuperset will
+	// panic.
+	IsProperSuperset(other Set[T]) bool
+
+	// IsSubset determines if every element in this set is in
+	// the other set.
+	//
+	// Note that the argument to IsSubset
+	// must be of the same type as the receiver
+	// of the method. Otherwise, IsSubset will
+	// panic.
+	IsSubset(other Set[T]) bool
+
+	// IsSuperset determines if every element in the other set
+	// is in this set.
+	//
+	// Note that the argument to IsSuperset
+	// must be of the same type as the receiver
+	// of the method. Otherwise, IsSuperset will
+	// panic.
+	IsSuperset(other Set[T]) bool
+
+	// Each iterates over elements and executes the passed func against each element.
+	// If passed func returns true, stop iteration at the time.
+	Each(func(T) bool)
+
+	// Iter returns a channel of elements that you can
+	// range over.
+	Iter() <-chan T
+
+	// Iterator returns an Iterator object that you can
+	// use to range over the set.
+	Iterator() *Iterator[T]
+
+	// Remove removes a single element from the set.
+	Remove(i T)
+
+	// RemoveAll removes multiple elements from the set.
+	RemoveAll(i ...T)
+
+	// String provides a convenient string representation
+	// of the current state of the set.
+	String() string
+
+	// SymmetricDifference returns a new set with all elements which are
+	// in either this set or the other set but not in both.
+	//
+	// Note that the argument to SymmetricDifference
+	// must be of the same type as the receiver
+	// of the method. Otherwise, SymmetricDifference
+	// will panic.
+	SymmetricDifference(other Set[T]) Set[T]
+
+	// Union returns a new set with all elements in both sets.
+	//
+	// Note that the argument to Union must be of the
+	// same type as the receiver of the method.
+	// Otherwise, IsSuperset will panic.
+	Union(other Set[T]) Set[T]
+
+	// Pop removes and returns an arbitrary item from the set.
+	Pop() (T, bool)
+
+	// ToSlice returns the members of the set as a slice.
+	ToSlice() []T
+
+	// MarshalJSON will marshal the set into a JSON-based representation.
+	MarshalJSON() ([]byte, error)
+
+	// UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure.
+	// For this to work, set subtypes must implemented the Marshal/Unmarshal interface.
+	UnmarshalJSON(b []byte) error
+}
+
+// NewSet creates and returns a new set with the given elements.
+// Operations on the resulting set are thread-safe.
+func NewSet[T comparable](vals ...T) Set[T] {
+	s := newThreadSafeSetWithSize[T](len(vals))
+	for _, item := range vals {
+		s.Add(item)
+	}
+	return s
+}
+
+// NewSetWithSize creates and returns a reference to an empty set with a specified
+// capacity. Operations on the resulting set are thread-safe.
+func NewSetWithSize[T comparable](cardinality int) Set[T] {
+	s := newThreadSafeSetWithSize[T](cardinality)
+	return s
+}
+
+// NewThreadUnsafeSet creates and returns a new set with the given elements.
+// Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] {
+	s := newThreadUnsafeSetWithSize[T](len(vals))
+	for _, item := range vals {
+		s.Add(item)
+	}
+	return s
+}
+
+// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with
+// a specified capacity. Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] {
+	s := newThreadUnsafeSetWithSize[T](cardinality)
+	return s
+}
+
+// NewSetFromMapKeys creates and returns a new set with the given keys of the map.
+// Operations on the resulting set are thread-safe.
+func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
+	s := NewSetWithSize[T](len(val))
+
+	for k := range val {
+		s.Add(k)
+	}
+
+	return s
+}
+
+// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map.
+// Operations on the resulting set are not thread-safe.
+func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
+	s := NewThreadUnsafeSetWithSize[T](len(val))
+
+	for k := range val {
+		s.Add(k)
+	}
+
+	return s
+}

+ 279 - 0
vendor/github.com/deckarep/golang-set/v2/threadsafe.go

@@ -0,0 +1,279 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package mapset
+
+import "sync"
+
+type threadSafeSet[T comparable] struct {
+	sync.RWMutex
+	uss threadUnsafeSet[T]
+}
+
+func newThreadSafeSet[T comparable]() *threadSafeSet[T] {
+	return &threadSafeSet[T]{
+		uss: newThreadUnsafeSet[T](),
+	}
+}
+
+func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] {
+	return &threadSafeSet[T]{
+		uss: newThreadUnsafeSetWithSize[T](cardinality),
+	}
+}
+
+func (t *threadSafeSet[T]) Add(v T) bool {
+	t.Lock()
+	ret := t.uss.Add(v)
+	t.Unlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Append(v ...T) int {
+	t.Lock()
+	ret := t.uss.Append(v...)
+	t.Unlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Contains(v ...T) bool {
+	t.RLock()
+	ret := t.uss.Contains(v...)
+	t.RUnlock()
+
+	return ret
+}
+
+func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	ret := t.uss.IsSubset(o.uss)
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	defer t.RUnlock()
+	o.RLock()
+	defer o.RUnlock()
+
+	return t.uss.IsProperSubset(o.uss)
+}
+
+func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool {
+	return other.IsSubset(t)
+}
+
+func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool {
+	return other.IsProperSubset(t)
+}
+
+func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T])
+	ret := &threadSafeSet[T]{uss: unsafeUnion}
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T])
+	ret := &threadSafeSet[T]{uss: unsafeIntersection}
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T])
+	ret := &threadSafeSet[T]{uss: unsafeDifference}
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T])
+	ret := &threadSafeSet[T]{uss: unsafeDifference}
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Clear() {
+	t.Lock()
+	t.uss.Clear()
+	t.Unlock()
+}
+
+func (t *threadSafeSet[T]) Remove(v T) {
+	t.Lock()
+	delete(t.uss, v)
+	t.Unlock()
+}
+
+func (t *threadSafeSet[T]) RemoveAll(i ...T) {
+	t.Lock()
+	t.uss.RemoveAll(i...)
+	t.Unlock()
+}
+
+func (t *threadSafeSet[T]) Cardinality() int {
+	t.RLock()
+	defer t.RUnlock()
+	return len(t.uss)
+}
+
+func (t *threadSafeSet[T]) Each(cb func(T) bool) {
+	t.RLock()
+	for elem := range t.uss {
+		if cb(elem) {
+			break
+		}
+	}
+	t.RUnlock()
+}
+
+func (t *threadSafeSet[T]) Iter() <-chan T {
+	ch := make(chan T)
+	go func() {
+		t.RLock()
+
+		for elem := range t.uss {
+			ch <- elem
+		}
+		close(ch)
+		t.RUnlock()
+	}()
+
+	return ch
+}
+
+func (t *threadSafeSet[T]) Iterator() *Iterator[T] {
+	iterator, ch, stopCh := newIterator[T]()
+
+	go func() {
+		t.RLock()
+	L:
+		for elem := range t.uss {
+			select {
+			case <-stopCh:
+				break L
+			case ch <- elem:
+			}
+		}
+		close(ch)
+		t.RUnlock()
+	}()
+
+	return iterator
+}
+
+func (t *threadSafeSet[T]) Equal(other Set[T]) bool {
+	o := other.(*threadSafeSet[T])
+
+	t.RLock()
+	o.RLock()
+
+	ret := t.uss.Equal(o.uss)
+	t.RUnlock()
+	o.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Clone() Set[T] {
+	t.RLock()
+
+	unsafeClone := t.uss.Clone().(threadUnsafeSet[T])
+	ret := &threadSafeSet[T]{uss: unsafeClone}
+	t.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) String() string {
+	t.RLock()
+	ret := t.uss.String()
+	t.RUnlock()
+	return ret
+}
+
+func (t *threadSafeSet[T]) Pop() (T, bool) {
+	t.Lock()
+	defer t.Unlock()
+	return t.uss.Pop()
+}
+
+func (t *threadSafeSet[T]) ToSlice() []T {
+	keys := make([]T, 0, t.Cardinality())
+	t.RLock()
+	for elem := range t.uss {
+		keys = append(keys, elem)
+	}
+	t.RUnlock()
+	return keys
+}
+
+func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) {
+	t.RLock()
+	b, err := t.uss.MarshalJSON()
+	t.RUnlock()
+
+	return b, err
+}
+
+func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error {
+	t.RLock()
+	err := t.uss.UnmarshalJSON(p)
+	t.RUnlock()
+
+	return err
+}

+ 325 - 0
vendor/github.com/deckarep/golang-set/v2/threadunsafe.go

@@ -0,0 +1,325 @@
+/*
+Open Source Initiative OSI - The MIT License (MIT):Licensing
+
+The MIT License (MIT)
+Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package mapset
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"strings"
+)
+
+type threadUnsafeSet[T comparable] map[T]struct{}
+
+// Assert concrete type:threadUnsafeSet adheres to Set interface.
+var _ Set[string] = (threadUnsafeSet[string])(nil)
+
+func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] {
+	return make(threadUnsafeSet[T])
+}
+
+func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] {
+	return make(threadUnsafeSet[T], cardinality)
+}
+
+func (s threadUnsafeSet[T]) Add(v T) bool {
+	prevLen := len(s)
+	s[v] = struct{}{}
+	return prevLen != len(s)
+}
+
+func (s threadUnsafeSet[T]) Append(v ...T) int {
+	prevLen := len(s)
+	for _, val := range v {
+		(s)[val] = struct{}{}
+	}
+	return len(s) - prevLen
+}
+
+// private version of Add which doesn't return a value
+func (s threadUnsafeSet[T]) add(v T) {
+	s[v] = struct{}{}
+}
+
+func (s threadUnsafeSet[T]) Cardinality() int {
+	return len(s)
+}
+
+func (s threadUnsafeSet[T]) Clear() {
+	// Constructions like this are optimised by compiler, and replaced by
+	// mapclear() function, defined in
+	// https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993)
+	for key := range s {
+		delete(s, key)
+	}
+}
+
+func (s threadUnsafeSet[T]) Clone() Set[T] {
+	clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality())
+	for elem := range s {
+		clonedSet.add(elem)
+	}
+	return clonedSet
+}
+
+func (s threadUnsafeSet[T]) Contains(v ...T) bool {
+	for _, val := range v {
+		if _, ok := s[val]; !ok {
+			return false
+		}
+	}
+	return true
+}
+
+// private version of Contains for a single element v
+func (s threadUnsafeSet[T]) contains(v T) (ok bool) {
+	_, ok = s[v]
+	return ok
+}
+
+func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] {
+	o := other.(threadUnsafeSet[T])
+
+	diff := newThreadUnsafeSet[T]()
+	for elem := range s {
+		if !o.contains(elem) {
+			diff.add(elem)
+		}
+	}
+	return diff
+}
+
+func (s threadUnsafeSet[T]) Each(cb func(T) bool) {
+	for elem := range s {
+		if cb(elem) {
+			break
+		}
+	}
+}
+
+func (s threadUnsafeSet[T]) Equal(other Set[T]) bool {
+	o := other.(threadUnsafeSet[T])
+
+	if s.Cardinality() != other.Cardinality() {
+		return false
+	}
+	for elem := range s {
+		if !o.contains(elem) {
+			return false
+		}
+	}
+	return true
+}
+
+func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] {
+	o := other.(threadUnsafeSet[T])
+
+	intersection := newThreadUnsafeSet[T]()
+	// loop over smaller set
+	if s.Cardinality() < other.Cardinality() {
+		for elem := range s {
+			if o.contains(elem) {
+				intersection.add(elem)
+			}
+		}
+	} else {
+		for elem := range o {
+			if s.contains(elem) {
+				intersection.add(elem)
+			}
+		}
+	}
+	return intersection
+}
+
+func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool {
+	return s.Cardinality() < other.Cardinality() && s.IsSubset(other)
+}
+
+func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool {
+	return s.Cardinality() > other.Cardinality() && s.IsSuperset(other)
+}
+
+func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool {
+	o := other.(threadUnsafeSet[T])
+	if s.Cardinality() > other.Cardinality() {
+		return false
+	}
+	for elem := range s {
+		if !o.contains(elem) {
+			return false
+		}
+	}
+	return true
+}
+
+func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool {
+	return other.IsSubset(s)
+}
+
+func (s threadUnsafeSet[T]) Iter() <-chan T {
+	ch := make(chan T)
+	go func() {
+		for elem := range s {
+			ch <- elem
+		}
+		close(ch)
+	}()
+
+	return ch
+}
+
+func (s threadUnsafeSet[T]) Iterator() *Iterator[T] {
+	iterator, ch, stopCh := newIterator[T]()
+
+	go func() {
+	L:
+		for elem := range s {
+			select {
+			case <-stopCh:
+				break L
+			case ch <- elem:
+			}
+		}
+		close(ch)
+	}()
+
+	return iterator
+}
+
+// Pop returns a popped item in case set is not empty, or nil-value of T
+// if set is already empty
+func (s threadUnsafeSet[T]) Pop() (v T, ok bool) {
+	for item := range s {
+		delete(s, item)
+		return item, true
+	}
+	return v, false
+}
+
+func (s threadUnsafeSet[T]) Remove(v T) {
+	delete(s, v)
+}
+
+func (s threadUnsafeSet[T]) RemoveAll(i ...T) {
+	for _, elem := range i {
+		delete(s, elem)
+	}
+}
+
+func (s threadUnsafeSet[T]) String() string {
+	items := make([]string, 0, len(s))
+
+	for elem := range s {
+		items = append(items, fmt.Sprintf("%v", elem))
+	}
+	return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
+}
+
+func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] {
+	o := other.(threadUnsafeSet[T])
+
+	sd := newThreadUnsafeSet[T]()
+	for elem := range s {
+		if !o.contains(elem) {
+			sd.add(elem)
+		}
+	}
+	for elem := range o {
+		if !s.contains(elem) {
+			sd.add(elem)
+		}
+	}
+	return sd
+}
+
+func (s threadUnsafeSet[T]) ToSlice() []T {
+	keys := make([]T, 0, s.Cardinality())
+	for elem := range s {
+		keys = append(keys, elem)
+	}
+
+	return keys
+}
+
+func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] {
+	o := other.(threadUnsafeSet[T])
+
+	n := s.Cardinality()
+	if o.Cardinality() > n {
+		n = o.Cardinality()
+	}
+	unionedSet := make(threadUnsafeSet[T], n)
+
+	for elem := range s {
+		unionedSet.add(elem)
+	}
+	for elem := range o {
+		unionedSet.add(elem)
+	}
+	return unionedSet
+}
+
+// MarshalJSON creates a JSON array from the set, it marshals all elements
+func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) {
+	items := make([]string, 0, s.Cardinality())
+
+	for elem := range s {
+		b, err := json.Marshal(elem)
+		if err != nil {
+			return nil, err
+		}
+
+		items = append(items, string(b))
+	}
+
+	return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
+}
+
+// UnmarshalJSON recreates a set from a JSON array, it only decodes
+// primitive types. Numbers are decoded as json.Number.
+func (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error {
+	var i []any
+
+	d := json.NewDecoder(bytes.NewReader(b))
+	d.UseNumber()
+	err := d.Decode(&i)
+	if err != nil {
+		return err
+	}
+
+	for _, v := range i {
+		switch t := v.(type) {
+		case T:
+			s.add(t)
+		default:
+			// anything else must be skipped.
+			continue
+		}
+	}
+
+	return nil
+}

+ 3 - 3
vendor/modules.txt

@@ -355,9 +355,9 @@ github.com/creack/pty
 # github.com/cyphar/filepath-securejoin v0.2.3
 ## explicit; go 1.13
 github.com/cyphar/filepath-securejoin
-# github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9
-## explicit
-github.com/deckarep/golang-set
+# github.com/deckarep/golang-set/v2 v2.3.0
+## explicit; go 1.18
+github.com/deckarep/golang-set/v2
 # github.com/dimchansky/utfbom v1.1.1
 ## explicit
 github.com/dimchansky/utfbom