From edda7e7ee82407842a04e9f9f0be92abcb8dd3f2 Mon Sep 17 00:00:00 2001 From: Oleg Lobanov Date: Tue, 11 Feb 2025 16:33:51 +0100 Subject: [PATCH] Upgrade to go 1.23 --- .github/workflows/build.yml | 18 +-- .golangci.yml | 124 ++++++++-------- decoder.go | 275 ++++++++++++++++++++---------------- decoder_test.go | 3 +- encoder.go | 37 ++--- encoder_test.go | 2 +- go.mod | 4 +- go.sum | 9 +- 8 files changed, 250 insertions(+), 222 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7fd1ece..e4a9073 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,21 +8,21 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@V5 with: - go-version: 1.19 - - uses: actions/checkout@v3 + go-version: 1.23 + - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: - version: v1.48.0 + version: v1.63 test: runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@V5 with: - go-version: 1.19 - - uses: actions/checkout@v3 + go-version: 1.23 + - uses: actions/checkout@v4 - run: go test -v -covermode=atomic -coverprofile=coverage.out ./... - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 785b995..c3cc808 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,100 +1,96 @@ run: timeout: 5m - go: '1.18' - -linters-settings: - dupl: - threshold: 100 - funlen: - lines: 100 - statements: 50 - goconst: - min-len: 2 - min-occurrences: 3 - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - disabled-checks: - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - ifElseChain - - octalLiteral - - whyNoLint - - wrapperFunc - - hugeParam - gocyclo: - min-complexity: 15 - goimports: - local-prefixes: github.com/o1egl/fwencoder - gomnd: - # don't include the "operation" and "assign" - checks: - - argument - - case - - condition - - return - ignored-numbers: - - '0' - - '1' - - '2' - - '3' - ignored-functions: - - strconv.Format* - - strconv.Parse* - - govet: - check-shadowing: true - lll: - line-length: 140 - misspell: - locale: US - nolintlint: - allow-leading-space: false - allow-unused: false # report any unused nolint directives - require-explanation: false # don't require an explanation for nolint directives - require-specific: false # don't require nolint directives to be specific about which linter is being skipped + go: '1.23' linters: disable-all: true enable: - bodyclose - - deadcode - - depguard + - copyloopvar - dogsled - dupl - errcheck - - exportloopref + - errorlint - funlen + - gocheckcompilerdirectives - gochecknoinits - goconst - gocritic - gocyclo + - godox - gofmt - goimports - - gomnd + - mnd - goprintffuncname - gosec - gosimple - govet + - intrange - ineffassign - lll - misspell - nakedret - noctx - nolintlint + - revive - staticcheck - - structcheck - stylecheck - - typecheck + - testifylint - unconvert - unparam - unused - - varcheck - whitespace +linters-settings: + goimports: + local-prefixes: github.com/o1egl/fwencoder + dupl: + threshold: 100 + funlen: + lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner. + statements: 50 + goconst: + min-len: 2 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + - whyNoLint + gocyclo: + min-complexity: 15 + godox: + keywords: + - FIXME + gofmt: + rewrite-rules: + - pattern: 'interface{}' + replacement: 'any' + errorlint: + asserts: false + lll: + line-length: 140 + misspell: + locale: US + nolintlint: + allow-unused: false # report any unused nolint directives + require-explanation: true # require an explanation for nolint directives + require-specific: true # require nolint directives to be specific about which linter is being skipped + revive: + rules: + - name: indent-error-flow + - name: unexported-return + disabled: true + - name: unused-parameter + - name: unused-receiver + issues: exclude-rules: - text: "shadow: declaration of \"err\" shadows declaration" @@ -103,7 +99,7 @@ issues: - path: _test\.go linters: - bodyclose - - gomnd + - mnd - noctx - goconst - funlen diff --git a/decoder.go b/decoder.go index 590a94f..0c29b46 100644 --- a/decoder.go +++ b/decoder.go @@ -10,7 +10,6 @@ import ( "reflect" "regexp" "runtime" - "sort" "strconv" "strings" "time" @@ -51,14 +50,12 @@ var ( // BDate time.Time `column:"Birthday" format:"2006/01/02"` // Postcode int `json:"Zip"` // } -func Unmarshal(data []byte, v interface{}) error { +func Unmarshal(data []byte, v any) error { return UnmarshalReader(bytes.NewReader(data), v) } // UnmarshalReader behaves the same as Unmarshal, but reads data from io.Reader -// -//nolint:gocyclo -func UnmarshalReader(reader io.Reader, v interface{}) (err error) { +func UnmarshalReader(reader io.Reader, v any) (err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { @@ -68,45 +65,49 @@ func UnmarshalReader(reader io.Reader, v interface{}) (err error) { } }() - sliceItemType := reflect.TypeOf(v) - if sliceItemType != nil && sliceItemType.Kind() == reflect.Ptr { - sliceItemType = sliceItemType.Elem() - } else { - return ErrIncorrectInputValue + sliceItemType, isSliceItemPtr, err := validateInput(v) + if err != nil { + return err } - if sliceItemType.Kind() == reflect.Slice { - sliceItemType = sliceItemType.Elem() - } else { - return ErrIncorrectInputValue - } + slice := reflect.ValueOf(v).Elem() + slice.Set(slice.Slice(0, 0)) + + return parseData(reader, slice, sliceItemType, isSliceItemPtr) +} - slice := reflect.ValueOf(v) - if slice.Kind() == reflect.Ptr { - slice = slice.Elem() +func validateInput(v any) (sliceItemType reflect.Type, isSliceItemPtr bool, err error) { + sliceItemType = reflect.TypeOf(v) + if sliceItemType == nil || sliceItemType.Kind() != reflect.Ptr { + return nil, false, ErrIncorrectInputValue } + sliceItemType = sliceItemType.Elem() - slice.Set(slice.Slice(0, 0)) + if sliceItemType.Kind() != reflect.Slice { + return nil, false, ErrIncorrectInputValue + } + sliceItemType = sliceItemType.Elem() - sliceType := sliceItemType - if sliceType.Kind() == reflect.Ptr { - sliceType = sliceType.Elem() + isSliceItemPtr = sliceItemType.Kind() == reflect.Ptr + if isSliceItemPtr { + sliceItemType = sliceItemType.Elem() } - if sliceType.Kind() != reflect.Struct { - return ErrIncorrectInputValue + if sliceItemType.Kind() != reflect.Struct { + return nil, false, ErrIncorrectInputValue } + return sliceItemType, isSliceItemPtr, nil +} + +func parseData(reader io.Reader, slice reflect.Value, sliceItemType reflect.Type, isSliceItemPtr bool) error { scanner := bufio.NewScanner(reader) - columnNames := getColumns(sliceType) - sort.Slice(columnNames, func(i, j int) bool { - return len([]rune(columnNames[i])) > len([]rune(columnNames[j])) - }) fieldsIndex := make(map[string]string) isHeaderParsed := false lineNum := 0 headersLength := 0 - columns := make([]fwColumn, 0, len(columnNames)) + columnNames := getColumns(sliceItemType) + var columns []fwColumn for scanner.Scan() { lineNum++ @@ -115,6 +116,7 @@ func UnmarshalReader(reader io.Reader, v interface{}) (err error) { if !isHeaderParsed { isHeaderParsed = true headersLength = len(lineRunes) + var err error columns, err = parseHeaders(line, columnNames) if err != nil { return err @@ -129,11 +131,11 @@ func UnmarshalReader(reader io.Reader, v interface{}) (err error) { fieldsIndex[prnColumn.name] = string(lineRunes[prnColumn.start:prnColumn.end]) } - newItem, err := createObject(fieldsIndex, sliceType) + newItem, err := createObject(fieldsIndex, sliceItemType) if err != nil { return fmt.Errorf("error in line %d: %w", lineNum, err) } - if sliceItemType.Kind() != reflect.Ptr { + if !isSliceItemPtr { newItem = newItem.Elem() } slice.Set(reflect.Append(slice, newItem)) @@ -142,7 +144,7 @@ func UnmarshalReader(reader io.Reader, v interface{}) (err error) { return nil } -func getRefName(field reflect.StructField) string { +func getRefName(field *reflect.StructField) string { if name, ok := field.Tag.Lookup(columnTagName); ok { return name } @@ -156,118 +158,43 @@ func createObject(fieldsIndex map[string]string, t reflect.Type) (reflect.Value, sp := reflect.New(t) s := sp.Elem() fieldsCount := s.NumField() - for fieldIndex := 0; fieldIndex < fieldsCount; fieldIndex++ { + for fieldIndex := range fieldsCount { currentField := s.Field(fieldIndex) typeField := s.Type().Field(fieldIndex) - refName := getRefName(typeField) + refName := getRefName(&typeField) rawValue, ok := fieldsIndex[refName] if !ok { continue } - if err := setFieldValue(currentField, typeField, rawValue); err != nil { + if err := setFieldValue(currentField, &typeField, rawValue); err != nil { return s, err } } return sp, nil } -//nolint:gocyclo,funlen -func setFieldValue(field reflect.Value, structField reflect.StructField, rawValue string) error { +func setFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string) error { rawValue = strings.TrimSpace(rawValue) fieldKind := field.Type().Kind() isPointer := fieldKind == reflect.Ptr if isPointer { fieldKind = field.Type().Elem().Kind() } - //nolint:dupl switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - value, err := strconv.ParseInt(rawValue, 10, 0) - if err != nil { - return newCastingError(err, rawValue, structField) - } - if isPointer { - v := reflect.New(field.Type().Elem()) - if v.Elem().OverflowInt(value) { - return newOverflowError(value, structField) - } - v.Elem().SetInt(value) - field.Set(v) - } else { - if field.OverflowInt(value) { - return newOverflowError(value, structField) - } - field.SetInt(value) - } + return setIntFieldValue(field, structField, rawValue, isPointer) case reflect.Float32, reflect.Float64: - value, err := strconv.ParseFloat(rawValue, 64) - if err != nil { - return newCastingError(err, rawValue, structField) - } - if isPointer { - v := reflect.New(field.Type().Elem()) - if v.Elem().OverflowFloat(value) { - return newOverflowError(value, structField) - } - v.Elem().SetFloat(value) - field.Set(v) - } else { - if field.OverflowFloat(value) { - return newOverflowError(value, structField) - } - field.SetFloat(value) - } + return setFloatFieldValue(field, structField, rawValue, isPointer) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - value, err := strconv.ParseUint(rawValue, 10, 64) - if err != nil { - return newCastingError(err, rawValue, structField) - } - if isPointer { - v := reflect.New(field.Type().Elem()) - if v.Elem().OverflowUint(value) { - return newOverflowError(value, structField) - } - v.Elem().SetUint(value) - field.Set(v) - } else { - if field.OverflowUint(value) { - return newOverflowError(value, structField) - } - field.SetUint(value) - } + return setUintFieldValue(field, structField, rawValue, isPointer) case reflect.String: - if isPointer { - field.Set(reflect.ValueOf(&rawValue)) - } else { - field.SetString(rawValue) - } + return setStringFieldValue(field, rawValue, isPointer) case reflect.Bool: - value, err := strconv.ParseBool(rawValue) - if err != nil { - return newCastingError(err, rawValue, structField) - } - if isPointer { - field.Set(reflect.ValueOf(&value)) - } else { - field.SetBool(value) - } + return setBoolFieldValue(field, structField, rawValue, isPointer) case reflect.Struct: if field.Type() == reflect.TypeOf(time.Time{}) || field.Type() == reflect.TypeOf(&time.Time{}) { - timeFormat, ok := structField.Tag.Lookup(format) - if !ok { - timeFormat = time.RFC3339 - } - t, err := time.Parse(timeFormat, rawValue) - if err != nil { - return newCastingError(err, rawValue, structField) - } - if isPointer { - field.Set(reflect.ValueOf(&t)) - } else { - field.Set(reflect.ValueOf(t)) - } - return nil + return setTimeFieldValue(field, structField, rawValue, isPointer) } fallthrough default: @@ -281,20 +208,124 @@ func setFieldValue(field reflect.Value, structField reflect.StructField, rawValu return nil } -func newCastingError(err error, rawValue string, structField reflect.StructField) error { +//nolint:dupl // it's not a duplicate +func setIntFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string, isPointer bool) error { + value, err := strconv.ParseInt(rawValue, 10, 0) + if err != nil { + return newCastingError(err, rawValue, structField) + } + if isPointer { + v := reflect.New(field.Type().Elem()) + if v.Elem().OverflowInt(value) { + return newOverflowError(value, structField) + } + v.Elem().SetInt(value) + field.Set(v) + } else { + if field.OverflowInt(value) { + return newOverflowError(value, structField) + } + field.SetInt(value) + } + return nil +} + +func setFloatFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string, isPointer bool) error { + value, err := strconv.ParseFloat(rawValue, 64) + if err != nil { + return newCastingError(err, rawValue, structField) + } + if isPointer { + v := reflect.New(field.Type().Elem()) + if v.Elem().OverflowFloat(value) { + return newOverflowError(value, structField) + } + v.Elem().SetFloat(value) + field.Set(v) + } else { + if field.OverflowFloat(value) { + return newOverflowError(value, structField) + } + field.SetFloat(value) + } + return nil +} + +//nolint:dupl // it's not a duplicate +func setUintFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string, isPointer bool) error { + value, err := strconv.ParseUint(rawValue, 10, 64) + if err != nil { + return newCastingError(err, rawValue, structField) + } + if isPointer { + v := reflect.New(field.Type().Elem()) + if v.Elem().OverflowUint(value) { + return newOverflowError(value, structField) + } + v.Elem().SetUint(value) + field.Set(v) + } else { + if field.OverflowUint(value) { + return newOverflowError(value, structField) + } + field.SetUint(value) + } + return nil +} + +func setStringFieldValue(field reflect.Value, rawValue string, isPointer bool) error { + if isPointer { + field.Set(reflect.ValueOf(&rawValue)) + } else { + field.SetString(rawValue) + } + return nil +} + +func setBoolFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string, isPointer bool) error { + value, err := strconv.ParseBool(rawValue) + if err != nil { + return newCastingError(err, rawValue, structField) + } + if isPointer { + field.Set(reflect.ValueOf(&value)) + } else { + field.SetBool(value) + } + return nil +} + +func setTimeFieldValue(field reflect.Value, structField *reflect.StructField, rawValue string, isPointer bool) error { + timeFormat, ok := structField.Tag.Lookup(format) + if !ok { + timeFormat = time.RFC3339 + } + t, err := time.Parse(timeFormat, rawValue) + if err != nil { + return newCastingError(err, rawValue, structField) + } + if isPointer { + field.Set(reflect.ValueOf(&t)) + } else { + field.Set(reflect.ValueOf(t)) + } + return nil +} + +func newCastingError(err error, rawValue string, structField *reflect.StructField) error { return fmt.Errorf(`filed casting "%s" to "%s:%v": %w`, rawValue, structField.Name, structField.Type, err) } -func newOverflowError(value any, structField reflect.StructField) error { +func newOverflowError(value any, structField *reflect.StructField) error { return fmt.Errorf(`value %v is too big for field %s:%v`, value, structField.Name, structField.Type) } func getColumns(sType reflect.Type) []string { fCount := sType.NumField() columnNames := make([]string, 0, fCount) - for i := 0; i < fCount; i++ { + for i := range fCount { field := sType.Field(i) - column := getRefName(field) + column := getRefName(&field) columnNames = append(columnNames, column) } return columnNames @@ -302,7 +333,7 @@ func getColumns(sType reflect.Type) []string { func parseHeaders(headerLine string, columnNames []string) ([]fwColumn, error) { columns := make([]fwColumn, 0, len(columnNames)) - for i := 0; i < len(columnNames); i++ { + for i := range columnNames { colName := columnNames[i] re, err := regexp.Compile(fmt.Sprintf("(%s *)", colName)) if err != nil { diff --git a/decoder_test.go b/decoder_test.go index b97194f..8c8b578 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type TestStruct struct { @@ -204,7 +205,7 @@ func TestIncorrectInput(t *testing.T) { } for _, err := range errs { - assert.EqualError(t, err, ErrIncorrectInputValue.Error()) + require.EqualError(t, err, ErrIncorrectInputValue.Error()) } type B struct { diff --git a/encoder.go b/encoder.go index 4d9fde3..ab9decb 100644 --- a/encoder.go +++ b/encoder.go @@ -43,14 +43,14 @@ func (c columnWidthMap) Set(name string, width uint64) { // BDate time.Time `column:"Birthday" format:"2006/01/02"` // Postcode int `json:"Zip"` // } -func Marshal(v interface{}) ([]byte, error) { +func Marshal(v any) ([]byte, error) { buf := bytes.Buffer{} err := MarshalWriter(&buf, v) return buf.Bytes(), err } // MarshalWriter behaves the same as Marshal, but write data into io.Writer -func MarshalWriter(writer io.Writer, v interface{}) (err error) { +func MarshalWriter(writer io.Writer, v any) (err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { @@ -100,18 +100,18 @@ func MarshalWriter(writer io.Writer, v interface{}) (err error) { } func writeData(writer io.Writer, slice reflect.Value, columnWidthIndex columnWidthMap) error { - for i := 0; i < slice.Len(); i++ { + for i := range slice.Len() { item := slice.Index(i) if item.Kind() == reflect.Ptr { item = item.Elem() } fieldsCount := item.NumField() - for fieldIndex := 0; fieldIndex < fieldsCount; fieldIndex++ { + for fieldIndex := range fieldsCount { fieldValue := item.Field(fieldIndex) fieldInfo := item.Type().Field(fieldIndex) - refName := getRefName(fieldInfo) + refName := getRefName(&fieldInfo) columnWidth := columnWidthIndex[refName] - if err := writeValue(writer, fieldValue, fieldInfo, columnWidth); err != nil { + if err := writeValue(writer, fieldValue, &fieldInfo, columnWidth); err != nil { return err } if fieldIndex != fieldsCount-1 { @@ -149,7 +149,7 @@ func writeHeader(writer io.Writer, columnNames []string, columnWidthIndex column func makeColumnWidthIndex(slice reflect.Value, columnNames []string) (columnWidthMap, error) { columnWidthIndex := make(columnWidthMap, len(columnNames)) - for i := 0; i < slice.Len(); i++ { + for i := range slice.Len() { item := slice.Index(i) if item.Kind() == reflect.Ptr { @@ -160,11 +160,11 @@ func makeColumnWidthIndex(slice reflect.Value, columnNames []string) (columnWidt } fieldsCount := item.NumField() - for fieldIndex := 0; fieldIndex < fieldsCount; fieldIndex++ { + for fieldIndex := range fieldsCount { currentField := item.Field(fieldIndex) typeField := item.Type().Field(fieldIndex) - refName := getRefName(typeField) - fieldLen, err := getFieldLen(currentField, typeField) + refName := getRefName(&typeField) + fieldLen, err := getFieldLen(currentField, &typeField) if err != nil { return nil, err } @@ -174,13 +174,13 @@ func makeColumnWidthIndex(slice reflect.Value, columnNames []string) (columnWidt return columnWidthIndex, nil } -//nolint:gocyclo -func writeValue(w io.Writer, value reflect.Value, field reflect.StructField, width uint64) error { +//nolint:gocyclo // it's ok +func writeValue(w io.Writer, value reflect.Value, field *reflect.StructField, width uint64) error { gap := strconv.FormatUint(width, 10) if value.Kind() == reflect.Ptr { if value.IsNil() { - for i := uint64(0); i < width; i++ { + for range width { if _, err := w.Write([]byte(" ")); err != nil { return err } @@ -241,7 +241,12 @@ func writeValue(w io.Writer, value reflect.Value, field reflect.StructField, wid return nil } -func getFieldLen(value reflect.Value, field reflect.StructField) (uint64, error) { +func getFieldLen(value reflect.Value, field *reflect.StructField) (uint64, error) { + const ( + trueLen = 4 + falseLen = 5 + ) + if value.Kind() == reflect.Ptr { if value.IsNil() { return 0, nil @@ -258,9 +263,9 @@ func getFieldLen(value reflect.Value, field reflect.StructField) (uint64, error) return uint64(len(strconv.FormatFloat(value.Float(), 'f', -1, 64))), nil case reflect.Bool: if value.Bool() { - return 4, nil //nolint:gomnd + return trueLen, nil } else { - return 5, nil //nolint:gomnd + return falseLen, nil } case reflect.String: return uint64(len(value.String())), nil diff --git a/encoder_test.go b/encoder_test.go index 46c38aa..06762aa 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -126,7 +126,7 @@ func TestMarshal_IncorrectInput(t *testing.T) { } } -func marshallErr(i interface{}) error { +func marshallErr(i any) error { _, err := Marshal(i) return err } diff --git a/go.mod b/go.mod index 1aeb507..53d1f9b 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/o1egl/fwencoder -go 1.18 +go 1.23 -require github.com/stretchr/testify v1.8.0 +require github.com/stretchr/testify v1.10.0 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 5164829..713a0b4 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,10 @@ -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=