diff --git a/.golangci.yml b/.golangci.yml index 657ff93a..a756424a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -65,6 +65,8 @@ linters: desc: "Use github.com/klauspost/compress instead of zlib" - pkg: "golang.org/x/exp/slices" desc: "Use 'slices' instead." + - pkg: "sort" + desc: "Use 'slices' instead." errcheck: exclude-functions: # Don't flag lines such as "io.Copy(io.Discard, resp.Body)". diff --git a/config/http_config.go b/config/http_config.go index d633479c..8c8f1c7c 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -71,10 +71,10 @@ type closeIdler interface { type TLSVersion uint16 var TLSVersions = map[string]TLSVersion{ - "TLS13": (TLSVersion)(tls.VersionTLS13), - "TLS12": (TLSVersion)(tls.VersionTLS12), - "TLS11": (TLSVersion)(tls.VersionTLS11), - "TLS10": (TLSVersion)(tls.VersionTLS10), + "TLS13": TLSVersion(tls.VersionTLS13), + "TLS12": TLSVersion(tls.VersionTLS12), + "TLS11": TLSVersion(tls.VersionTLS11), + "TLS10": TLSVersion(tls.VersionTLS10), } func (tv *TLSVersion) UnmarshalYAML(unmarshal func(any) error) error { @@ -639,11 +639,13 @@ func NewRoundTripperFromConfigWithContext(ctx context.Context, cfg HTTPClientCon dialContext = conntrack.NewDialContextFunc( conntrack.DialWithDialContextFunc((func(context.Context, string, string) (net.Conn, error))(opts.dialContextFunc)), conntrack.DialWithTracing(), - conntrack.DialWithName(name)) + conntrack.DialWithName(name), + ) } else { dialContext = conntrack.NewDialContextFunc( conntrack.DialWithTracing(), - conntrack.DialWithName(name)) + conntrack.DialWithName(name), + ) } newRT := func(tlsConfig *tls.Config) (http.RoundTripper, error) { diff --git a/config/tls_config_test.go b/config/tls_config_test.go index cffe1320..ca949048 100644 --- a/config/tls_config_test.go +++ b/config/tls_config_test.go @@ -133,7 +133,7 @@ func TestInvalidTLSConfig(t *testing.T) { } func TestTLSVersionStringer(t *testing.T) { - s := (TLSVersion)(tls.VersionTLS13) + s := TLSVersion(tls.VersionTLS13) require.Equalf(t, "TLS13", s.String(), "tls.VersionTLS13 string should be TLS13, got %s", s.String()) } diff --git a/expfmt/decode_test.go b/expfmt/decode_test.go index 917a4a1c..edc79bdd 100644 --- a/expfmt/decode_test.go +++ b/expfmt/decode_test.go @@ -23,7 +23,7 @@ import ( "net/http" "os" "reflect" - "sort" + "slices" "strings" "testing" @@ -99,8 +99,8 @@ mf2 4 require.NoError(t, err) all = append(all, smpls...) } - sort.Sort(all) - sort.Sort(out) + slices.SortFunc(all, (*model.Sample).Compare) + slices.SortFunc(out, (*model.Sample).Compare) require.Truef(t, reflect.DeepEqual(all, out), "output does not match") } @@ -399,8 +399,8 @@ func TestProtoDecoder(t *testing.T) { require.NoError(t, err) all = append(all, smpls...) } - sort.Sort(all) - sort.Sort(scenario.expected) + slices.SortFunc(all, (*model.Sample).Compare) + slices.SortFunc(scenario.expected, (*model.Sample).Compare) require.Truef(t, reflect.DeepEqual(all, scenario.expected), "%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) } } diff --git a/model/alert.go b/model/alert.go index 460f554f..d62321b7 100644 --- a/model/alert.go +++ b/model/alert.go @@ -110,17 +110,25 @@ func (a *Alert) Validate() error { // Alert is a list of alerts that can be sorted in chronological order. type Alerts []*Alert -func (as Alerts) Len() int { return len(as) } -func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] } - -func (as Alerts) Less(i, j int) bool { - if as[i].StartsAt.Before(as[j].StartsAt) { - return true - } - if as[i].EndsAt.Before(as[j].EndsAt) { - return true +// Compare implements the cmp.Comparator interface for Alert pointers. +// It compares by StartsAt, then EndsAt, then Fingerprint. +func (a *Alert) Compare(o *Alert) int { + switch { + case a.StartsAt.Before(o.StartsAt): + return -1 + case o.StartsAt.Before(a.StartsAt): + return 1 + case a.EndsAt.Before(o.EndsAt): + return -1 + case o.EndsAt.Before(a.EndsAt): + return 1 + case a.Fingerprint() < o.Fingerprint(): + return -1 + case a.Fingerprint() > o.Fingerprint(): + return 1 + default: + return 0 } - return as[i].Fingerprint() < as[j].Fingerprint() } // HasFiring returns true iff one of the alerts is not resolved. diff --git a/model/alert_test.go b/model/alert_test.go index fc3eaf10..6847c903 100644 --- a/model/alert_test.go +++ b/model/alert_test.go @@ -15,7 +15,7 @@ package model import ( "fmt" - "sort" + "slices" "strings" "testing" "time" @@ -251,11 +251,11 @@ func TestSortAlerts(t *testing.T) { }, } - sort.Sort(alerts) + slices.SortFunc(alerts, (*Alert).Compare) expected := []string{ - "DiskFull[5ffe595][resolved]", "InternalError[09cfd46][resolved]", + "DiskFull[5ffe595][resolved]", "OutOfMemory[d43a602][resolved]", "DiskFull[5ff4595][resolved]", "OutOfMemory[d444602][resolved]", diff --git a/model/fingerprinting.go b/model/fingerprinting.go index fc4de410..c03825c5 100644 --- a/model/fingerprinting.go +++ b/model/fingerprinting.go @@ -42,24 +42,9 @@ func (f Fingerprint) String() string { } // Fingerprints represents a collection of Fingerprint subject to a given -// natural sorting scheme. It implements sort.Interface. +// natural sorting scheme. type Fingerprints []Fingerprint -// Len implements sort.Interface. -func (f Fingerprints) Len() int { - return len(f) -} - -// Less implements sort.Interface. -func (f Fingerprints) Less(i, j int) bool { - return f[i] < f[j] -} - -// Swap implements sort.Interface. -func (f Fingerprints) Swap(i, j int) { - f[i], f[j] = f[j], f[i] -} - // FingerprintSet is a set of Fingerprints. type FingerprintSet map[Fingerprint]struct{} diff --git a/model/fingerprinting_test.go b/model/fingerprinting_test.go index f4a4a3d5..99853888 100644 --- a/model/fingerprinting_test.go +++ b/model/fingerprinting_test.go @@ -14,7 +14,8 @@ package model import ( - "sort" + "cmp" + "slices" "testing" ) @@ -52,7 +53,7 @@ func TestFingerprintsSort(t *testing.T) { 18446744073709551615, } - sort.Sort(fingerPrints) + slices.SortFunc(fingerPrints, cmp.Compare) expected := Fingerprints{ 0, diff --git a/model/labels.go b/model/labels.go index 29688a13..49853ef3 100644 --- a/model/labels.go +++ b/model/labels.go @@ -149,21 +149,9 @@ func (ln *LabelName) UnmarshalJSON(b []byte) error { return nil } -// LabelNames is a sortable LabelName slice. In implements sort.Interface. +// LabelNames is a sortable LabelName slice. type LabelNames []LabelName -func (l LabelNames) Len() int { - return len(l) -} - -func (l LabelNames) Less(i, j int) bool { - return l[i] < l[j] -} - -func (l LabelNames) Swap(i, j int) { - l[i], l[j] = l[j], l[i] -} - func (l LabelNames) String() string { labelStrings := make([]string, 0, len(l)) for _, label := range l { @@ -180,50 +168,31 @@ func (lv LabelValue) IsValid() bool { return utf8.ValidString(string(lv)) } -// LabelValues is a sortable LabelValue slice. It implements sort.Interface. +// LabelValues is a sortable LabelValue slice. type LabelValues []LabelValue -func (l LabelValues) Len() int { - return len(l) -} - -func (l LabelValues) Less(i, j int) bool { - return string(l[i]) < string(l[j]) -} - -func (l LabelValues) Swap(i, j int) { - l[i], l[j] = l[j], l[i] -} - // LabelPair pairs a name with a value. type LabelPair struct { Name LabelName Value LabelValue } -// LabelPairs is a sortable slice of LabelPair pointers. It implements -// sort.Interface. -type LabelPairs []*LabelPair - -func (l LabelPairs) Len() int { - return len(l) -} - -func (l LabelPairs) Less(i, j int) bool { +// Compare implements the cmp.Comparator interface for LabelPair pointers. +// It compares by Name, then by Value. +func (l *LabelPair) Compare(o *LabelPair) int { switch { - case l[i].Name > l[j].Name: - return false - case l[i].Name < l[j].Name: - return true - case l[i].Value > l[j].Value: - return false - case l[i].Value < l[j].Value: - return true + case l.Name < o.Name: + return -1 + case l.Name > o.Name: + return 1 + case l.Value < o.Value: + return -1 + case l.Value > o.Value: + return 1 default: - return false + return 0 } } -func (l LabelPairs) Swap(i, j int) { - l[i], l[j] = l[j], l[i] -} +// LabelPairs is a sortable slice of LabelPair pointers. +type LabelPairs []*LabelPair diff --git a/model/labels_test.go b/model/labels_test.go index 31717b4e..5ea9962b 100644 --- a/model/labels_test.go +++ b/model/labels_test.go @@ -14,8 +14,9 @@ package model import ( + "cmp" "fmt" - "sort" + "slices" "testing" ) @@ -35,7 +36,7 @@ func testLabelNames(t testing.TB) { } for i, scenario := range scenarios { - sort.Sort(scenario.in) + slices.SortFunc(scenario.in, cmp.Compare) for j, expected := range scenario.out { if expected != scenario.in[j] { @@ -71,7 +72,7 @@ func testLabelValues(t testing.TB) { } for i, scenario := range scenarios { - sort.Sort(scenario.in) + slices.SortFunc(scenario.in, cmp.Compare) for j, expected := range scenario.out { if expected != scenario.in[j] { @@ -211,7 +212,7 @@ func TestSortLabelPairs(t *testing.T) { }, } - sort.Sort(labelPairs) + slices.SortFunc(labelPairs, (*LabelPair).Compare) expectedLabelPairs := LabelPairs{ { diff --git a/model/labelset.go b/model/labelset.go index 6010b26a..8eb24e9b 100644 --- a/model/labelset.go +++ b/model/labelset.go @@ -14,10 +14,11 @@ package model import ( + "cmp" "encoding/json" "fmt" "maps" - "sort" + "slices" ) // A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet @@ -85,7 +86,7 @@ func (ls LabelSet) Before(o LabelSet) bool { lns = append(lns, ln) } // It's probably not worth it to de-dup lns. - sort.Sort(lns) + slices.SortFunc(lns, cmp.Compare) for _, ln := range lns { mlv, ok := ls[ln] if !ok { diff --git a/model/metric.go b/model/metric.go index 2fe46151..49da2af3 100644 --- a/model/metric.go +++ b/model/metric.go @@ -19,7 +19,7 @@ import ( "fmt" "maps" "regexp" - "sort" + "slices" "strconv" "strings" "unicode/utf8" @@ -283,7 +283,7 @@ func (m Metric) String() string { } return "{}" default: - sort.Strings(labelStrings) + slices.Sort(labelStrings) return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", ")) } } diff --git a/model/value.go b/model/value.go index 8dffd9c4..ed01373a 100644 --- a/model/value.go +++ b/model/value.go @@ -16,7 +16,7 @@ package model import ( "encoding/json" "fmt" - "sort" + "slices" "strconv" "strings" ) @@ -132,31 +132,26 @@ func (s *Sample) UnmarshalJSON(b []byte) error { return nil } -// Samples is a sortable Sample slice. It implements sort.Interface. +// Samples is a sortable Sample slice. type Samples []*Sample -func (s Samples) Len() int { - return len(s) -} - -// Less compares first the metrics, then the timestamp. -func (s Samples) Less(i, j int) bool { +// Compare implements the cmp.Comparator interface for Sample pointers. +// It compares first the metrics, then the timestamp. +func (s *Sample) Compare(o *Sample) int { switch { - case s[i].Metric.Before(s[j].Metric): - return true - case s[j].Metric.Before(s[i].Metric): - return false - case s[i].Timestamp.Before(s[j].Timestamp): - return true + case s.Metric.Before(o.Metric): + return -1 + case o.Metric.Before(s.Metric): + return 1 + case s.Timestamp.Before(o.Timestamp): + return -1 + case o.Timestamp.Before(s.Timestamp): + return 1 default: - return false + return 0 } } -func (s Samples) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - // Equal compares two sets of samples and returns true if they are equal. func (s Samples) Equal(o Samples) bool { if len(s) != len(o) { @@ -178,6 +173,18 @@ type SampleStream struct { Histograms []SampleHistogramPair `json:"histograms"` } +// Compare implements the cmp.Comparator interface for SampleStream pointers. +func (ss *SampleStream) Compare(o *SampleStream) int { + switch { + case ss.Metric.Before(o.Metric): + return -1 + case o.Metric.Before(ss.Metric): + return 1 + default: + return 0 + } +} + func (ss SampleStream) String() string { valuesLength := len(ss.Values) vals := make([]string, valuesLength+len(ss.Histograms)) @@ -312,23 +319,6 @@ func (vec Vector) String() string { return strings.Join(entries, "\n") } -func (vec Vector) Len() int { return len(vec) } -func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] } - -// Less compares first the metrics, then the timestamp. -func (vec Vector) Less(i, j int) bool { - switch { - case vec[i].Metric.Before(vec[j].Metric): - return true - case vec[j].Metric.Before(vec[i].Metric): - return false - case vec[i].Timestamp.Before(vec[j].Timestamp): - return true - default: - return false - } -} - // Equal compares two sets of samples and returns true if they are equal. func (vec Vector) Equal(o Vector) bool { if len(vec) != len(o) { @@ -346,14 +336,10 @@ func (vec Vector) Equal(o Vector) bool { // Matrix is a list of time series. type Matrix []*SampleStream -func (m Matrix) Len() int { return len(m) } -func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) } -func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] } - func (m Matrix) String() string { matCp := make(Matrix, len(m)) copy(matCp, m) - sort.Sort(matCp) + slices.SortFunc(matCp, (*SampleStream).Compare) strs := make([]string, len(matCp)) diff --git a/model/value_test.go b/model/value_test.go index 1f1d8278..65e86889 100644 --- a/model/value_test.go +++ b/model/value_test.go @@ -16,7 +16,7 @@ package model import ( "encoding/json" "math" - "sort" + "slices" "testing" "github.com/stretchr/testify/require" @@ -347,7 +347,7 @@ func TestVectorSort(t *testing.T) { }, } - sort.Sort(input) + slices.SortFunc(input, (*Sample).Compare) for i, actual := range input { actualFp := actual.Metric.Fingerprint() diff --git a/route/route_test.go b/route/route_test.go index 689cfaa3..61387ab4 100644 --- a/route/route_test.go +++ b/route/route_test.go @@ -159,17 +159,20 @@ func TestInstrumentations(t *testing.T) { func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "1"+handlerName) return handler - }). + }, + ). WithInstrumentation( func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "2"+handlerName) return handler - }). + }, + ). WithInstrumentation( func(handlerName string, handler http.HandlerFunc) http.HandlerFunc { got = append(got, "3"+handlerName) return handler - }), + }, + ), want: []string{"1/foo", "2/foo", "3/foo"}, }, }