From 728254ab96cba8f464a67630de464f5c6fbf153b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:16:16 +0000 Subject: [PATCH] Bump github.com/deckarep/golang-set/v2 from 2.3.1 to 2.7.0 Bumps [github.com/deckarep/golang-set/v2](https://github.com/deckarep/golang-set) from 2.3.1 to 2.7.0. - [Release notes](https://github.com/deckarep/golang-set/releases) - [Commits](https://github.com/deckarep/golang-set/compare/v2.3.1...v2.7.0) --- updated-dependencies: - dependency-name: github.com/deckarep/golang-set/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../deckarep/golang-set/v2/README.md | 17 +++ .../github.com/deckarep/golang-set/v2/set.go | 12 +- .../deckarep/golang-set/v2/sorted.go | 42 ++++++ .../deckarep/golang-set/v2/threadsafe.go | 36 +++-- .../deckarep/golang-set/v2/threadunsafe.go | 125 ++++++++++-------- vendor/modules.txt | 2 +- 8 files changed, 166 insertions(+), 74 deletions(-) create mode 100644 vendor/github.com/deckarep/golang-set/v2/sorted.go diff --git a/go.mod b/go.mod index 9d64b00dd..1c6f09cd5 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ toolchain go1.22.2 require ( github.com/apparentlymart/go-cidr v1.1.0 - github.com/deckarep/golang-set/v2 v2.3.1 + github.com/deckarep/golang-set/v2 v2.7.0 github.com/golangci/golangci-lint v1.56.1 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/terraform-plugin-docs v0.16.0 diff --git a/go.sum b/go.sum index d4e6ab9eb..163809ce9 100644 --- a/go.sum +++ b/go.sum @@ -263,8 +263,8 @@ github.com/daixiang0/gci v0.12.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiE 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/v2 v2.3.1 h1:vjmkvJt/IV27WXPyYQpAh4bRyWJc5Y435D17XQ9QU5A= -github.com/deckarep/golang-set/v2 v2.3.1/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU= github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md index 9aef202c9..bb691b1ca 100644 --- a/vendor/github.com/deckarep/golang-set/v2/README.md +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -6,6 +6,15 @@ The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. +## Psst +* Hi there, 👋! Do you use or have interest in the [Zig programming language](https://ziglang.org/) created by Andrew Kelley? If so, the golang-set project has a new sibling project: [ziglang-set](https://github.com/deckarep/ziglang-set)! Come check it out! + +## Update 12/3/2024 +* Packaged version: `2.7.0` fixes a long-standing bug with *JSON Unmarshaling*. A large refactor in the interest of performance +introduced this bug and there was no way around it but to revert the code back to how it was previously. The performance +difference was likely negligible to begin with. JSON Marshaling and Unmarshaling is now properly supported again without +needing to do workarounds. + ## 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 @@ -17,6 +26,14 @@ The missing `generic` set collection for the Go language. Until Go has sets bui 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. +## Install + +Use `go get` to install this package. + +```shell +go get github.com/deckarep/golang-set/v2 +``` + ## Features * *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go index d6b042c8a..292089dcf 100644 --- a/vendor/github.com/deckarep/golang-set/v2/set.go +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -62,6 +62,13 @@ type Set[T comparable] interface { // are all in the set. Contains(val ...T) bool + // ContainsOne returns whether the given item + // is in the set. + // + // Contains may cause the argument to escape to the heap. + // See: https://github.com/deckarep/golang-set/issues/118 + ContainsOne(val T) bool + // ContainsAny returns whether at least one of the // given items are in the set. ContainsAny(val ...T) bool @@ -97,6 +104,9 @@ type Set[T comparable] interface { // panic. Intersect(other Set[T]) Set[T] + // IsEmpty determines if there are elements in the set. + IsEmpty() bool + // IsProperSubset determines if every element in this set is in // the other set but the two sets are not equal. // @@ -169,7 +179,7 @@ type Set[T comparable] interface { // // Note that the argument to Union must be of the // same type as the receiver of the method. - // Otherwise, IsSuperset will panic. + // Otherwise, Union will panic. Union(other Set[T]) Set[T] // Pop removes and returns an arbitrary item from the set. diff --git a/vendor/github.com/deckarep/golang-set/v2/sorted.go b/vendor/github.com/deckarep/golang-set/v2/sorted.go new file mode 100644 index 000000000..8ee2e7076 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/sorted.go @@ -0,0 +1,42 @@ +//go:build go1.21 +// +build go1.21 + +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2023 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 ( + "cmp" + "slices" +) + +// Sorted returns a sorted slice of a set of any ordered type in ascending order. +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sorted[E cmp.Ordered](set Set[E]) []E { + s := set.ToSlice() + slices.Sort(s) + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go index 067d09a89..93f20c86c 100644 --- a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -29,7 +29,7 @@ import "sync" type threadSafeSet[T comparable] struct { sync.RWMutex - uss threadUnsafeSet[T] + uss *threadUnsafeSet[T] } func newThreadSafeSet[T comparable]() *threadSafeSet[T] { @@ -66,6 +66,14 @@ func (t *threadSafeSet[T]) Contains(v ...T) bool { return ret } +func (t *threadSafeSet[T]) ContainsOne(v T) bool { + t.RLock() + ret := t.uss.ContainsOne(v) + t.RUnlock() + + return ret +} + func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { t.RLock() ret := t.uss.ContainsAny(v...) @@ -74,6 +82,10 @@ func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { return ret } +func (t *threadSafeSet[T]) IsEmpty() bool { + return t.Cardinality() == 0 +} + func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { o := other.(*threadSafeSet[T]) @@ -111,7 +123,7 @@ func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { t.RLock() o.RLock() - unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T]) + unsafeUnion := t.uss.Union(o.uss).(*threadUnsafeSet[T]) ret := &threadSafeSet[T]{uss: unsafeUnion} t.RUnlock() o.RUnlock() @@ -124,7 +136,7 @@ func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { t.RLock() o.RLock() - unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T]) + unsafeIntersection := t.uss.Intersect(o.uss).(*threadUnsafeSet[T]) ret := &threadSafeSet[T]{uss: unsafeIntersection} t.RUnlock() o.RUnlock() @@ -137,7 +149,7 @@ func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { t.RLock() o.RLock() - unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T]) + unsafeDifference := t.uss.Difference(o.uss).(*threadUnsafeSet[T]) ret := &threadSafeSet[T]{uss: unsafeDifference} t.RUnlock() o.RUnlock() @@ -150,7 +162,7 @@ func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { t.RLock() o.RLock() - unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T]) + unsafeDifference := t.uss.SymmetricDifference(o.uss).(*threadUnsafeSet[T]) ret := &threadSafeSet[T]{uss: unsafeDifference} t.RUnlock() o.RUnlock() @@ -165,7 +177,7 @@ func (t *threadSafeSet[T]) Clear() { func (t *threadSafeSet[T]) Remove(v T) { t.Lock() - delete(t.uss, v) + delete(*t.uss, v) t.Unlock() } @@ -178,12 +190,12 @@ func (t *threadSafeSet[T]) RemoveAll(i ...T) { func (t *threadSafeSet[T]) Cardinality() int { t.RLock() defer t.RUnlock() - return len(t.uss) + return len(*t.uss) } func (t *threadSafeSet[T]) Each(cb func(T) bool) { t.RLock() - for elem := range t.uss { + for elem := range *t.uss { if cb(elem) { break } @@ -196,7 +208,7 @@ func (t *threadSafeSet[T]) Iter() <-chan T { go func() { t.RLock() - for elem := range t.uss { + for elem := range *t.uss { ch <- elem } close(ch) @@ -212,7 +224,7 @@ func (t *threadSafeSet[T]) Iterator() *Iterator[T] { go func() { t.RLock() L: - for elem := range t.uss { + for elem := range *t.uss { select { case <-stopCh: break L @@ -241,7 +253,7 @@ func (t *threadSafeSet[T]) Equal(other Set[T]) bool { func (t *threadSafeSet[T]) Clone() Set[T] { t.RLock() - unsafeClone := t.uss.Clone().(threadUnsafeSet[T]) + unsafeClone := t.uss.Clone().(*threadUnsafeSet[T]) ret := &threadSafeSet[T]{uss: unsafeClone} t.RUnlock() return ret @@ -263,7 +275,7 @@ func (t *threadSafeSet[T]) Pop() (T, bool) { func (t *threadSafeSet[T]) ToSlice() []T { keys := make([]T, 0, t.Cardinality()) t.RLock() - for elem := range t.uss { + for elem := range *t.uss { keys = append(keys, elem) } t.RUnlock() diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go index f53c42e01..7e3243b20 100644 --- a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -34,14 +34,16 @@ import ( type threadUnsafeSet[T comparable] map[T]struct{} // Assert concrete type:threadUnsafeSet adheres to Set interface. -var _ Set[string] = (threadUnsafeSet[string])(nil) +var _ Set[string] = (*threadUnsafeSet[string])(nil) -func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] { - return make(threadUnsafeSet[T]) +func newThreadUnsafeSet[T comparable]() *threadUnsafeSet[T] { + t := make(threadUnsafeSet[T]) + return &t } -func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] { - return make(threadUnsafeSet[T], cardinality) +func newThreadUnsafeSetWithSize[T comparable](cardinality int) *threadUnsafeSet[T] { + t := make(threadUnsafeSet[T], cardinality) + return &t } func (s threadUnsafeSet[T]) Add(v T) bool { @@ -50,52 +52,57 @@ func (s threadUnsafeSet[T]) Add(v T) bool { return prevLen != len(s) } -func (s threadUnsafeSet[T]) Append(v ...T) int { - prevLen := len(s) +func (s *threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(*s) for _, val := range v { - (s)[val] = struct{}{} + (*s)[val] = struct{}{} } - return len(s) - prevLen + 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]) add(v T) { + (*s)[v] = struct{}{} } -func (s threadUnsafeSet[T]) Cardinality() int { - return len(s) +func (s *threadUnsafeSet[T]) Cardinality() int { + return len(*s) } -func (s threadUnsafeSet[T]) Clear() { +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) + for key := range *s { + delete(*s, key) } } -func (s threadUnsafeSet[T]) Clone() Set[T] { +func (s *threadUnsafeSet[T]) Clone() Set[T] { clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) - for elem := range s { + for elem := range *s { clonedSet.add(elem) } return clonedSet } -func (s threadUnsafeSet[T]) Contains(v ...T) bool { +func (s *threadUnsafeSet[T]) Contains(v ...T) bool { for _, val := range v { - if _, ok := s[val]; !ok { + if _, ok := (*s)[val]; !ok { return false } } return true } -func (s threadUnsafeSet[T]) ContainsAny(v ...T) bool { +func (s *threadUnsafeSet[T]) ContainsOne(v T) bool { + _, ok := (*s)[v] + return ok +} + +func (s *threadUnsafeSet[T]) ContainsAny(v ...T) bool { for _, val := range v { - if _, ok := s[val]; ok { + if _, ok := (*s)[val]; ok { return true } } @@ -103,16 +110,16 @@ func (s threadUnsafeSet[T]) ContainsAny(v ...T) bool { } // private version of Contains for a single element v -func (s threadUnsafeSet[T]) contains(v T) (ok bool) { - _, ok = s[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]) +func (s *threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) diff := newThreadUnsafeSet[T]() - for elem := range s { + for elem := range *s { if !o.contains(elem) { diff.add(elem) } @@ -120,21 +127,21 @@ func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { return diff } -func (s threadUnsafeSet[T]) Each(cb func(T) bool) { - for elem := range s { +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]) +func (s *threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadUnsafeSet[T]) if s.Cardinality() != other.Cardinality() { return false } - for elem := range s { + for elem := range *s { if !o.contains(elem) { return false } @@ -142,19 +149,19 @@ func (s threadUnsafeSet[T]) Equal(other Set[T]) bool { return true } -func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { - o := other.(threadUnsafeSet[T]) +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 { + for elem := range *s { if o.contains(elem) { intersection.add(elem) } } } else { - for elem := range o { + for elem := range *o { if s.contains(elem) { intersection.add(elem) } @@ -163,20 +170,24 @@ func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { return intersection } -func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { +func (s *threadUnsafeSet[T]) IsEmpty() bool { + return s.Cardinality() == 0 +} + +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 { +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]) +func (s *threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadUnsafeSet[T]) if s.Cardinality() > other.Cardinality() { return false } - for elem := range s { + for elem := range *s { if !o.contains(elem) { return false } @@ -184,14 +195,14 @@ func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool { return true } -func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { +func (s *threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { return other.IsSubset(s) } -func (s threadUnsafeSet[T]) Iter() <-chan T { +func (s *threadUnsafeSet[T]) Iter() <-chan T { ch := make(chan T) go func() { - for elem := range s { + for elem := range *s { ch <- elem } close(ch) @@ -200,12 +211,12 @@ func (s threadUnsafeSet[T]) Iter() <-chan T { return ch } -func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { +func (s *threadUnsafeSet[T]) Iterator() *Iterator[T] { iterator, ch, stopCh := newIterator[T]() go func() { L: - for elem := range s { + for elem := range *s { select { case <-stopCh: break L @@ -220,9 +231,9 @@ func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { // 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) +func (s *threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range *s { + delete(*s, item) return item, true } return v, false @@ -247,16 +258,16 @@ func (s threadUnsafeSet[T]) String() string { return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) } -func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { - o := other.(threadUnsafeSet[T]) +func (s *threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) sd := newThreadUnsafeSet[T]() - for elem := range s { + for elem := range *s { if !o.contains(elem) { sd.add(elem) } } - for elem := range o { + for elem := range *o { if !s.contains(elem) { sd.add(elem) } @@ -274,7 +285,7 @@ func (s threadUnsafeSet[T]) ToSlice() []T { } func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { - o := other.(threadUnsafeSet[T]) + o := other.(*threadUnsafeSet[T]) n := s.Cardinality() if o.Cardinality() > n { @@ -285,10 +296,10 @@ func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { for elem := range s { unionedSet.add(elem) } - for elem := range o { + for elem := range *o { unionedSet.add(elem) } - return unionedSet + return &unionedSet } // MarshalJSON creates a JSON array from the set, it marshals all elements @@ -309,7 +320,7 @@ func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { // 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 { +func (s *threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { var i []T err := json.Unmarshal(b, &i) if err != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 35c8d3925..3243211ae 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -387,7 +387,7 @@ github.com/daixiang0/gci/pkg/utils # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/deckarep/golang-set/v2 v2.3.1 +# github.com/deckarep/golang-set/v2 v2.7.0 ## explicit; go 1.18 github.com/deckarep/golang-set/v2 # github.com/denis-tingaikin/go-header v0.4.3