Skip to content

Commit 3865112

Browse files
Refactor: Use pkg/set types for internal set collections (#2193)
1 parent affa1da commit 3865112

9 files changed

Lines changed: 831 additions & 48 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,16 @@ SERVER_ENDPOINT=localhost:9000 ACCESS_KEY=minioadmin SECRET_KEY=minioadmin ENABL
2929
### Linting and Code Quality
3030

3131
```bash
32-
# Run all checks (lint, vet, test, examples, functional tests)
32+
# Run all checks (lint, test, examples, functional tests)
3333
make checks
3434

35-
# Run linter only
35+
# Run linter only (includes govet, staticcheck, and other linters)
3636
make lint
3737

38-
# Run vet and staticcheck
39-
make vet
40-
41-
# Alternative: run golangci-lint directly
38+
# Run golangci-lint directly
4239
golangci-lint run --timeout=5m --config ./.golangci.yml
40+
41+
# Note: 'make vet' is now an alias for 'make lint' for backwards compatibility
4342
```
4443

4544
### Building Examples

Makefile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ all: checks
55

66
.PHONY: examples docs
77

8-
checks: lint vet test examples functional-test
8+
checks: lint test examples functional-test
99

1010
lint:
1111
@mkdir -p ${GOPATH}/bin
@@ -14,10 +14,7 @@ lint:
1414
@GO111MODULE=on ${GOPATH}/bin/golangci-lint cache clean
1515
@GO111MODULE=on ${GOPATH}/bin/golangci-lint run --timeout=5m --config ./.golangci.yml
1616

17-
vet:
18-
@GO111MODULE=on go vet ./...
19-
@echo "Installing staticcheck" && go install honnef.co/go/tools/cmd/staticcheck@latest
20-
${GOPATH}/bin/staticcheck -tests=false -checks="all,-ST1000,-ST1003,-ST1016,-ST1020,-ST1021,-ST1022,-ST1023,-ST1005"
17+
vet: lint
2118

2219
test:
2320
@GO111MODULE=on SERVER_ENDPOINT=localhost:9000 ACCESS_KEY=minioadmin SECRET_KEY=minioadmin ENABLE_HTTPS=1 MINT_MODE=full go test -race -v ./...

api-error-response.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string)
129129
Server: resp.Header.Get("Server"),
130130
}
131131

132-
_, success := successStatus[resp.StatusCode]
132+
success := successStatus.Contains(resp.StatusCode)
133133

134134
errBody, err := xmlDecodeAndBody(resp.Body, &errResp)
135135
// Xml decoding failed with no body, fall back to HTTP headers.

api.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
"github.com/minio/minio-go/v7/pkg/credentials"
4444
"github.com/minio/minio-go/v7/pkg/kvcache"
4545
"github.com/minio/minio-go/v7/pkg/s3utils"
46+
"github.com/minio/minio-go/v7/pkg/set"
4647
"github.com/minio/minio-go/v7/pkg/signer"
4748
"github.com/minio/minio-go/v7/pkg/singleflight"
4849
"golang.org/x/net/publicsuffix"
@@ -636,11 +637,11 @@ func (c *Client) do(req *http.Request) (resp *http.Response, err error) {
636637
}
637638

638639
// List of success status.
639-
var successStatus = map[int]struct{}{
640-
http.StatusOK: {},
641-
http.StatusNoContent: {},
642-
http.StatusPartialContent: {},
643-
}
640+
var successStatus = set.CreateIntSet(
641+
http.StatusOK,
642+
http.StatusNoContent,
643+
http.StatusPartialContent,
644+
)
644645

645646
// executeMethod - instantiates a given method, and retries the
646647
// request upon any error up to maxRetries attempts in a binomially
@@ -722,7 +723,7 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ
722723
return nil, err
723724
}
724725

725-
_, success := successStatus[res.StatusCode]
726+
success := successStatus.Contains(res.StatusCode)
726727
if success && !metadata.expect200OKWithError {
727728
// We do not expect 2xx to return an error return.
728729
return res, nil

pkg/set/intset.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
3+
* Copyright 2015-2026 MinIO, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package set
19+
20+
import (
21+
"encoding/json"
22+
"fmt"
23+
)
24+
25+
// IntSet - uses map as set of ints.
26+
// This is now implemented using the generic Set[int] type.
27+
type IntSet Set[int]
28+
29+
// ToSlice - returns IntSet as int slice.
30+
func (set IntSet) ToSlice() []int {
31+
return ToSliceOrdered(Set[int](set))
32+
}
33+
34+
// IsEmpty - returns whether the set is empty or not.
35+
func (set IntSet) IsEmpty() bool {
36+
return Set[int](set).IsEmpty()
37+
}
38+
39+
// Add - adds int to the set.
40+
func (set IntSet) Add(i int) {
41+
Set[int](set).Add(i)
42+
}
43+
44+
// Remove - removes int in the set. It does nothing if int does not exist in the set.
45+
func (set IntSet) Remove(i int) {
46+
Set[int](set).Remove(i)
47+
}
48+
49+
// Contains - checks if int is in the set.
50+
func (set IntSet) Contains(i int) bool {
51+
return Set[int](set).Contains(i)
52+
}
53+
54+
// FuncMatch - returns new set containing each value who passes match function.
55+
// A 'matchFn' should accept element in a set as first argument and
56+
// 'matchInt' as second argument. The function can do any logic to
57+
// compare both the arguments and should return true to accept element in
58+
// a set to include in output set else the element is ignored.
59+
func (set IntSet) FuncMatch(matchFn func(int, int) bool, matchInt int) IntSet {
60+
return IntSet(Set[int](set).FuncMatch(matchFn, matchInt))
61+
}
62+
63+
// ApplyFunc - returns new set containing each value processed by 'applyFn'.
64+
// A 'applyFn' should accept element in a set as a argument and return
65+
// a processed int. The function can do any logic to return a processed
66+
// int.
67+
func (set IntSet) ApplyFunc(applyFn func(int) int) IntSet {
68+
return IntSet(Set[int](set).ApplyFunc(applyFn))
69+
}
70+
71+
// Equals - checks whether given set is equal to current set or not.
72+
func (set IntSet) Equals(iset IntSet) bool {
73+
return Set[int](set).Equals(Set[int](iset))
74+
}
75+
76+
// Intersection - returns the intersection with given set as new set.
77+
func (set IntSet) Intersection(iset IntSet) IntSet {
78+
return IntSet(Set[int](set).Intersection(Set[int](iset)))
79+
}
80+
81+
// Difference - returns the difference with given set as new set.
82+
func (set IntSet) Difference(iset IntSet) IntSet {
83+
return IntSet(Set[int](set).Difference(Set[int](iset)))
84+
}
85+
86+
// Union - returns the union with given set as new set.
87+
func (set IntSet) Union(iset IntSet) IntSet {
88+
return IntSet(Set[int](set).Union(Set[int](iset)))
89+
}
90+
91+
// MarshalJSON - converts to JSON data.
92+
func (set IntSet) MarshalJSON() ([]byte, error) {
93+
return json.Marshal(set.ToSlice())
94+
}
95+
96+
// UnmarshalJSON - parses JSON data and creates new set with it.
97+
func (set *IntSet) UnmarshalJSON(data []byte) error {
98+
sl := []int{}
99+
var err error
100+
if err = json.Unmarshal(data, &sl); err == nil {
101+
*set = make(IntSet)
102+
for _, i := range sl {
103+
set.Add(i)
104+
}
105+
}
106+
return err
107+
}
108+
109+
// String - returns printable string of the set.
110+
func (set IntSet) String() string {
111+
return fmt.Sprintf("%v", set.ToSlice())
112+
}
113+
114+
// NewIntSet - creates new int set.
115+
func NewIntSet() IntSet {
116+
return IntSet(New[int]())
117+
}
118+
119+
// CreateIntSet - creates new int set with given int values.
120+
func CreateIntSet(il ...int) IntSet {
121+
return IntSet(Create(il...))
122+
}
123+
124+
// CopyIntSet - returns copy of given set.
125+
func CopyIntSet(set IntSet) IntSet {
126+
return IntSet(Copy(Set[int](set)))
127+
}

pkg/set/intset_test.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
3+
* Copyright 2015-2026 MinIO, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package set
19+
20+
import (
21+
"reflect"
22+
"testing"
23+
)
24+
25+
// IntSet.MarshalJSON() is called with series of cases for valid and erroneous inputs and the result is validated.
26+
func TestIntSetMarshalJSON(t *testing.T) {
27+
testCases := []struct {
28+
set IntSet
29+
expectedResult string
30+
}{
31+
// Test set with values.
32+
{CreateIntSet(1, 2, 3), `[1,2,3]`},
33+
// Test empty set.
34+
{NewIntSet(), "[]"},
35+
}
36+
37+
for _, testCase := range testCases {
38+
if result, _ := testCase.set.MarshalJSON(); string(result) != testCase.expectedResult {
39+
t.Fatalf("expected: %s, got: %s", testCase.expectedResult, string(result))
40+
}
41+
}
42+
}
43+
44+
// IntSet.UnmarshalJSON() is called with series of cases for valid and erroneous inputs and the result is validated.
45+
func TestIntSetUnmarshalJSON(t *testing.T) {
46+
testCases := []struct {
47+
data []byte
48+
expectedResult string
49+
}{
50+
// Test to convert JSON array to set.
51+
{[]byte(`[1,2,3]`), `[1 2 3]`},
52+
// Test to convert JSON empty array to set.
53+
{[]byte(`[]`), `[]`},
54+
}
55+
56+
for _, testCase := range testCases {
57+
var set IntSet
58+
set.UnmarshalJSON(testCase.data)
59+
if result := set.String(); result != testCase.expectedResult {
60+
t.Fatalf("expected: %s, got: %s", testCase.expectedResult, result)
61+
}
62+
}
63+
}
64+
65+
// IntSet.String() is called with series of cases for valid and erroneous inputs and the result is validated.
66+
func TestIntSetString(t *testing.T) {
67+
testCases := []struct {
68+
set IntSet
69+
expectedResult string
70+
}{
71+
// Test empty set.
72+
{NewIntSet(), `[]`},
73+
// Test set with value.
74+
{CreateIntSet(42), `[42]`},
75+
// Test set with multiple values.
76+
{CreateIntSet(1, 2, 3), `[1 2 3]`},
77+
}
78+
79+
for _, testCase := range testCases {
80+
if str := testCase.set.String(); str != testCase.expectedResult {
81+
t.Fatalf("expected: %s, got: %s", testCase.expectedResult, str)
82+
}
83+
}
84+
}
85+
86+
// IntSet.ToSlice() is called with series of cases for valid and erroneous inputs and the result is validated.
87+
func TestIntSetToSlice(t *testing.T) {
88+
testCases := []struct {
89+
set IntSet
90+
expectedResult []int
91+
}{
92+
// Test empty set.
93+
{NewIntSet(), []int{}},
94+
// Test set with value.
95+
{CreateIntSet(42), []int{42}},
96+
// Test set with multiple values (should be sorted).
97+
{CreateIntSet(3, 1, 2), []int{1, 2, 3}},
98+
}
99+
100+
for _, testCase := range testCases {
101+
islice := testCase.set.ToSlice()
102+
if !reflect.DeepEqual(islice, testCase.expectedResult) {
103+
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, islice)
104+
}
105+
}
106+
}
107+
108+
func TestIntSet_UnmarshalJSON(t *testing.T) {
109+
type args struct {
110+
data []byte
111+
expectResult []int
112+
}
113+
tests := []struct {
114+
name string
115+
set IntSet
116+
args args
117+
wantErr bool
118+
}{
119+
{
120+
name: "test ints",
121+
set: NewIntSet(),
122+
args: args{
123+
data: []byte(`[1,2,3]`),
124+
expectResult: []int{1, 2, 3},
125+
},
126+
wantErr: false,
127+
},
128+
{
129+
name: "test negative ints",
130+
set: NewIntSet(),
131+
args: args{
132+
data: []byte(`[-1,-2,0,1,2]`),
133+
expectResult: []int{-2, -1, 0, 1, 2},
134+
},
135+
wantErr: false,
136+
},
137+
{
138+
name: "test empty array",
139+
set: NewIntSet(),
140+
args: args{
141+
data: []byte(`[]`),
142+
expectResult: []int{},
143+
},
144+
wantErr: false,
145+
},
146+
}
147+
for _, tt := range tests {
148+
t.Run(tt.name, func(t *testing.T) {
149+
if err := tt.set.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
150+
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
151+
}
152+
slice := tt.set.ToSlice()
153+
if !reflect.DeepEqual(slice, tt.args.expectResult) {
154+
t.Errorf("IntSet() get %v, want %v", slice, tt.args.expectResult)
155+
}
156+
})
157+
}
158+
}

0 commit comments

Comments
 (0)