refactor: migrate to Go 1.22 slices/cmp, update linters

Replace usage of golang.org/x/exp/constraints with Go 1.22 cmp/slices.
Update .golangci.yml to new v2 format and enable gofmt/goimports.
Refactor imports and type constraints across codebase for consistency.
This commit is contained in:
Ruidy 2025-09-01 23:08:10 -04:00
parent 39be9420c4
commit c53d46816f
No known key found for this signature in database
GPG key ID: 705C24D202990805
14 changed files with 83 additions and 69 deletions

View file

@ -1,42 +1,54 @@
skip-dirs-use-default: true version: "2"
run:
timeout: 5m
linters: linters:
enable: enable:
- bodyclose - bodyclose
- deadcode
- depguard - depguard
- dogsled - dogsled
- errcheck
- errorlint - errorlint
- exportloopref
- gocritic - gocritic
- gocyclo - gocyclo
- gofmt
- goimports
- goprintffuncname - goprintffuncname
- gosimple
- gosec - gosec
- govet
- ineffassign
- misspell - misspell
- noctx - noctx
- nolintlint - nolintlint
- prealloc - prealloc
- rowserrcheck - rowserrcheck
- staticcheck - staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert - unconvert
- unparam - unparam
- unused
- varcheck
- whitespace - whitespace
fast: true exclusions:
generated: lax
linters-settings: presets:
goimports: - comments
local-prefixes: github.com/rjNemo/underscore - common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
settings:
depguard:
rules:
main:
list-mode: lax
files:
- $all
allow:
- $gostd
- github.com/rjNemo/underscore
- github.com/rjNemo/underscore/...
- github.com/stretchr/testify/...
- golang.org/x/exp/constraints
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

9
any.go
View file

@ -1,12 +1,9 @@
package underscore package underscore
import "slices"
// Any returns true if any of the values in the slice pass the predicate truth test. // Any returns true if any of the values in the slice pass the predicate truth test.
// Short-circuits and stops traversing the slice if a true element is found. // Short-circuits and stops traversing the slice if a true element is found.
func Any[T any](values []T, predicate func(T) bool) bool { func Any[T any](values []T, predicate func(T) bool) bool {
for _, v := range values { return slices.ContainsFunc(values, predicate)
if predicate(v) {
return true
}
}
return false
} }

View file

@ -9,7 +9,6 @@ import (
) )
func TestDrop(t *testing.T) { func TestDrop(t *testing.T) {
nums := []int{1, 9, 2, 8, 3, 7, 4, 6, 5} nums := []int{1, 9, 2, 8, 3, 7, 4, 6, 5}
want := []int{1, 9, 2, 3, 7, 4, 6, 5} want := []int{1, 9, 2, 3, 7, 4, 6, 5}

View file

@ -3,8 +3,9 @@ package underscore_test
import ( import (
"testing" "testing"
u "github.com/rjNemo/underscore"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
u "github.com/rjNemo/underscore"
) )
func TestFind(t *testing.T) { func TestFind(t *testing.T) {

View file

@ -3,8 +3,9 @@ package underscore_test
import ( import (
"testing" "testing"
u "github.com/rjNemo/underscore"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
u "github.com/rjNemo/underscore"
) )
func TestFlatmap(t *testing.T) { func TestFlatmap(t *testing.T) {

View file

@ -3,9 +3,9 @@ package underscore_test
import ( import (
"testing" "testing"
u "github.com/rjNemo/underscore"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
u "github.com/rjNemo/underscore"
) )
func TestIntersection(t *testing.T) { func TestIntersection(t *testing.T) {

14
join.go
View file

@ -6,12 +6,12 @@ func Join[T, P any, S comparable](
left []T, left []T,
right []P, right []P,
leftSelector func(T) S, leftSelector func(T) S,
rightSelector func(P) S) []Tuple[T, []P] { rightSelector func(P) S,
) []Tuple[T, []P] {
var results = make([]Tuple[T, []P], 0, len(left)) results := make([]Tuple[T, []P], 0, len(left))
for _, l := range left { for _, l := range left {
var matches = Filter(right, func(r P) bool { return leftSelector(l) == rightSelector(r) }) matches := Filter(right, func(r P) bool { return leftSelector(l) == rightSelector(r) })
var tuple = Tuple[T, []P]{Left: l, Right: matches} tuple := Tuple[T, []P]{Left: l, Right: matches}
results = append(results, tuple) results = append(results, tuple)
} }
@ -28,8 +28,8 @@ func JoinProject[L, R, O any, S comparable](
right []R, right []R,
leftSelector func(L) S, leftSelector func(L) S,
rightSelector func(R) S, rightSelector func(R) S,
projection func(Tuple[L, []R]) O) (results []O) { projection func(Tuple[L, []R]) O,
) (results []O) {
for _, x := range Join(left, right, leftSelector, rightSelector) { for _, x := range Join(left, right, leftSelector, rightSelector) {
results = append(results, projection(x)) results = append(results, projection(x))
} }

View file

@ -3,23 +3,26 @@ package underscore_test
import ( import (
"testing" "testing"
u "github.com/rjNemo/underscore"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
u "github.com/rjNemo/underscore"
) )
var zero = u.Tuple[int, string]{Left: 0, Right: "Zero"} var (
var one = u.Tuple[int, string]{Left: 1, Right: "One"} zero = u.Tuple[int, string]{Left: 0, Right: "Zero"}
var two = u.Tuple[int, string]{Left: 2, Right: "Two"} one = u.Tuple[int, string]{Left: 1, Right: "One"}
var three = u.Tuple[int, string]{Left: 3, Right: "Three"} two = u.Tuple[int, string]{Left: 2, Right: "Two"}
three = u.Tuple[int, string]{Left: 3, Right: "Three"}
)
func Test_Join_Can_Join_Two_Slices_Together(t *testing.T) { func Test_Join_Can_Join_Two_Slices_Together(t *testing.T) {
var left = []u.Tuple[int, string]{zero, one, two, three} left := []u.Tuple[int, string]{zero, one, two, three}
var right = []u.Tuple[int, string]{one, three, two, three, two, three} right := []u.Tuple[int, string]{one, three, two, three, two, three}
selector := func(x u.Tuple[int, string]) int { return x.Left } selector := func(x u.Tuple[int, string]) int { return x.Left }
var joined = u.Join(left, right, selector, selector) joined := u.Join(left, right, selector, selector)
var want = []u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]{ want := []u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]{
{Left: zero, Right: nil}, {Left: zero, Right: nil},
{Left: one, Right: []u.Tuple[int, string]{one}}, {Left: one, Right: []u.Tuple[int, string]{one}},
{Left: two, Right: []u.Tuple[int, string]{two, two}}, {Left: two, Right: []u.Tuple[int, string]{two, two}},
@ -30,16 +33,16 @@ func Test_Join_Can_Join_Two_Slices_Together(t *testing.T) {
} }
func Test_Join_Can_Join_and_Project_Two_Slices_Together(t *testing.T) { func Test_Join_Can_Join_and_Project_Two_Slices_Together(t *testing.T) {
var left = []u.Tuple[int, string]{zero, one, two, three} left := []u.Tuple[int, string]{zero, one, two, three}
var right = []u.Tuple[int, string]{one, three, two, three, two, three} right := []u.Tuple[int, string]{one, three, two, three, two, three}
selector := func(x u.Tuple[int, string]) int { return x.Left } selector := func(x u.Tuple[int, string]) int { return x.Left }
project := func(x u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]) int { project := func(x u.Tuple[u.Tuple[int, string], []u.Tuple[int, string]]) int {
return len(x.Right) // projecting to a could of how many return len(x.Right) // projecting to a could of how many
} }
var joined = u.JoinProject(left, right, selector, selector, project) joined := u.JoinProject(left, right, selector, selector, project)
var want = []int{0, 1, 2, 3} want := []int{0, 1, 2, 3}
assert.Equal(t, want, joined) assert.Equal(t, want, joined)
} }

4
max.go
View file

@ -1,11 +1,11 @@
package underscore package underscore
import "golang.org/x/exp/constraints" import "cmp"
// Max returns the maximum value in the slice. // Max returns the maximum value in the slice.
// This function can currently only compare numbers reliably. // This function can currently only compare numbers reliably.
// This function uses operator <. // This function uses operator <.
func Max[T constraints.Ordered](values []T) T { func Max[T cmp.Ordered](values []T) T {
max := values[0] max := values[0]
for _, v := range values { for _, v := range values {
if v > max { if v > max {

4
min.go
View file

@ -1,11 +1,11 @@
package underscore package underscore
import "golang.org/x/exp/constraints" import "cmp"
// Min returns the minimum value in the slice. // Min returns the minimum value in the slice.
// This function can currently only compare numbers reliably. // This function can currently only compare numbers reliably.
// This function uses operator <. // This function uses operator <.
func Min[T constraints.Ordered](values []T) T { func Min[T cmp.Ordered](values []T) T {
min := values[0] min := values[0]
for _, v := range values { for _, v := range values {
if v < min { if v < min {

View file

@ -1,10 +1,10 @@
package underscore package underscore
import ( import (
"golang.org/x/exp/constraints" "cmp"
) )
type Pipe[T constraints.Ordered] struct { type Pipe[T cmp.Ordered] struct {
Value []T Value []T
} }
@ -12,7 +12,7 @@ type Pipe[T constraints.Ordered] struct {
// call Value to retrieve the final value. // call Value to retrieve the final value.
// //
// Methods not returning a slice such as Reduce, All, Any, will break the Pipe and return Value instantly. // Methods not returning a slice such as Reduce, All, Any, will break the Pipe and return Value instantly.
func NewPipe[T constraints.Ordered](value []T) Pipe[T] { func NewPipe[T cmp.Ordered](value []T) Pipe[T] {
return Pipe[T]{Value: value} return Pipe[T]{Value: value}
} }

View file

@ -2,7 +2,7 @@ package underscore
// Result represent the outcome of an operation where failure is possible // Result represent the outcome of an operation where failure is possible
type Result[T any] interface { type Result[T any] interface {
isResult() //to seal the Result interface isResult() // to seal the Result interface
ToValue() (*T, error) ToValue() (*T, error)
IsSuccess() bool IsSuccess() bool
} }

View file

@ -1,20 +1,19 @@
package underscore package underscore
import ( import (
"cmp"
"sort" "sort"
"golang.org/x/exp/constraints"
) )
// SortSliceASC sorts any slice ASCENDING // SortSliceASC sorts any slice ASCENDING
func SortSliceASC[T constraints.Ordered](s []T) { func SortSliceASC[T cmp.Ordered](s []T) {
sort.SliceStable(s, func(i, j int) bool { sort.SliceStable(s, func(i, j int) bool {
return s[i] < s[j] return s[i] < s[j]
}) })
} }
// SortSliceDESC sorts any slice DESCENDING // SortSliceDESC sorts any slice DESCENDING
func SortSliceDESC[T constraints.Ordered](s []T) { func SortSliceDESC[T cmp.Ordered](s []T) {
sort.SliceStable(s, func(i, j int) bool { sort.SliceStable(s, func(i, j int) bool {
return s[i] > s[j] return s[i] > s[j]
}) })

8
sum.go
View file

@ -1,9 +1,11 @@
package underscore package underscore
import "golang.org/x/exp/constraints" import (
"cmp"
)
// Sum adds elements of the slice. // Sum adds elements of the slice.
func Sum[T constraints.Ordered](values []T) (sum T) { func Sum[T cmp.Ordered](values []T) (sum T) {
for _, v := range values { for _, v := range values {
sum += v sum += v
} }
@ -12,7 +14,7 @@ func Sum[T constraints.Ordered](values []T) (sum T) {
// SumMap sums the values you select from your struct, basically a sort cut instead of // SumMap sums the values you select from your struct, basically a sort cut instead of
// having to perform a [Map] followed by a [Sum]. // having to perform a [Map] followed by a [Sum].
func SumMap[T any, R constraints.Ordered](list []T, selector func(T) R) (sum R) { func SumMap[T any, R cmp.Ordered](list []T, selector func(T) R) (sum R) {
for _, v := range list { for _, v := range list {
sum += selector(v) sum += selector(v)
} }