diff --git a/Makefile b/Makefile index 3bac5c0a..e7408569 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,11 @@ fmtcheck: .PHONY: lint lint: fmtcheck - docker run --env=GOFLAGS=-mod=vendor --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:v2.0.2 golangci-lint -v run + docker run --env=GOFLAGS=-mod=vendor --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:v2.5.0 golangci-lint -v run .PHONY: deeplint deeplint: fmtcheck - docker run --env=GOFLAGS=-mod=vendor --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:v2.0.2 golangci-lint run --exclude-use-default=false --enable-all -D dupl --build-tags integration + docker run --env=GOFLAGS=-mod=vendor --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:v2.5.0 golangci-lint run --exclude-use-default=false --enable-all -D dupl --build-tags integration .PHONY: deps-start deps-start: diff --git a/go.mod b/go.mod index 237600f0..c505b457 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/beatlabs/harvester go 1.25.0 require ( - github.com/hashicorp/consul/api v1.32.1 + github.com/hashicorp/consul/api v1.32.4 github.com/hashicorp/go-hclog v1.6.3 github.com/redis/go-redis/v9 v9.12.1 github.com/stretchr/testify v1.11.1 @@ -15,6 +15,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.18.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -23,11 +24,9 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/serf v0.10.2 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect diff --git a/go.sum b/go.sum index 0e83b48e..97deb2a2 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -36,6 +35,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -57,10 +58,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= -github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= -github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= -github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= +github.com/hashicorp/consul/api v1.32.4 h1:xNe27KcBNYHbqWX/6c6WTAlPoZlZv8onDEySmjcspO0= +github.com/hashicorp/consul/api v1.32.4/go.mod h1:jy0q71iTvUGfbCwo+ExBF0gEesE5cY2TSeAz2EoNG8E= +github.com/hashicorp/consul/sdk v0.16.3 h1:kI/oax+yeaoremkh36G/f4Q13ivdFF4AE+Co/LlZa0Q= +github.com/hashicorp/consul/sdk v0.16.3/go.mod h1:TSPshuYdi1OQwpLund2vkTHpp4WnLyhf7Q/YihGMtp0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -124,8 +125,6 @@ github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -191,8 +190,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig new file mode 100644 index 00000000..faef0c91 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab + +[{Makefile,*.mk}] +indent_style = tab + +[*.nix] +indent_size = 2 + +[.golangci.yaml] +indent_size = 2 diff --git a/vendor/github.com/go-viper/mapstructure/v2/.envrc b/vendor/github.com/go-viper/mapstructure/v2/.envrc new file mode 100644 index 00000000..2e0f9f5f --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.envrc @@ -0,0 +1,4 @@ +if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" +fi +use flake . --impure diff --git a/vendor/github.com/go-viper/mapstructure/v2/.gitignore b/vendor/github.com/go-viper/mapstructure/v2/.gitignore new file mode 100644 index 00000000..470e7ca2 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.gitignore @@ -0,0 +1,6 @@ +/.devenv/ +/.direnv/ +/.pre-commit-config.yaml +/bin/ +/build/ +/var/ diff --git a/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml new file mode 100644 index 00000000..bda96256 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml @@ -0,0 +1,48 @@ +version: "2" + +run: + timeout: 10m + +linters: + enable: + - govet + - ineffassign + # - misspell + - nolintlint + # - revive + + disable: + - errcheck + - staticcheck + - unused + + settings: + misspell: + locale: US + nolintlint: + allow-unused: false # report any unused nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + # - golines + + settings: + gci: + sections: + - standard + - default + - localmodule + gofmt: + simplify: true + rewrite-rules: + - pattern: interface{} + replacement: any + + exclusions: + paths: + - internal/ diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md similarity index 92% rename from vendor/github.com/mitchellh/mapstructure/CHANGELOG.md rename to vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md index c7582349..afd44e5f 100644 --- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md +++ b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md @@ -1,3 +1,11 @@ +> [!WARNING] +> As of v2 of this library, change log can be found in GitHub releases. + +## 1.5.1 + +* Wrap errors so they're compatible with `errors.Is` and `errors.As` [GH-282] +* Fix map of slices not decoding properly in certain cases. [GH-266] + ## 1.5.0 * New option `IgnoreUntaggedFields` to ignore decoding to any fields diff --git a/vendor/github.com/mitchellh/mapstructure/LICENSE b/vendor/github.com/go-viper/mapstructure/v2/LICENSE similarity index 100% rename from vendor/github.com/mitchellh/mapstructure/LICENSE rename to vendor/github.com/go-viper/mapstructure/v2/LICENSE diff --git a/vendor/github.com/go-viper/mapstructure/v2/README.md b/vendor/github.com/go-viper/mapstructure/v2/README.md new file mode 100644 index 00000000..45db7197 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/README.md @@ -0,0 +1,81 @@ +# mapstructure + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/go-viper/mapstructure/ci.yaml?style=flat-square)](https://github.com/go-viper/mapstructure/actions/workflows/ci.yaml) +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/go-viper/mapstructure?style=flat-square&color=61CFDD) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-viper/mapstructure/badge?style=flat-square)](https://deps.dev/go/github.com%252Fgo-viper%252Fmapstructure%252Fv2) + +mapstructure is a Go library for decoding generic map values to structures +and vice versa, while providing helpful error handling. + +This library is most useful when decoding values from some data stream (JSON, +Gob, etc.) where you don't _quite_ know the structure of the underlying data +until you read a part of it. You can therefore read a `map[string]interface{}` +and use this library to decode it into the proper underlying native Go +structure. + +## Installation + +```shell +go get github.com/go-viper/mapstructure/v2 +``` + +## Migrating from `github.com/mitchellh/mapstructure` + +[@mitchehllh](https://github.com/mitchellh) announced his intent to archive some of his unmaintained projects (see [here](https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc) and [here](https://github.com/mitchellh/mapstructure/issues/349)). This is a repository achieved the "blessed fork" status. + +You can migrate to this package by changing your import paths in your Go files to `github.com/go-viper/mapstructure/v2`. +The API is the same, so you don't need to change anything else. + +Here is a script that can help you with the migration: + +```shell +sed -i 's|github.com/mitchellh/mapstructure|github.com/go-viper/mapstructure/v2|g' $(find . -type f -name '*.go') +``` + +If you need more time to migrate your code, that is absolutely fine. + +Some of the latest fixes are backported to the v1 release branch of this package, so you can use the Go modules `replace` feature until you are ready to migrate: + +```shell +replace github.com/mitchellh/mapstructure => github.com/go-viper/mapstructure v1.6.0 +``` + +## Usage & Example + +For usage and examples see the [documentation](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2). + +The `Decode` function has examples associated with it there. + +## But Why?! + +Go offers fantastic standard libraries for decoding formats such as JSON. +The standard method is to have a struct pre-created, and populate that struct +from the bytes of the encoded format. This is great, but the problem is if +you have configuration or an encoding that changes slightly depending on +specific fields. For example, consider this JSON: + +```json +{ + "type": "person", + "name": "Mitchell" +} +``` + +Perhaps we can't populate a specific structure without first reading +the "type" field from the JSON. We could always do two passes over the +decoding of the JSON (reading the "type" first, and the rest later). +However, it is much simpler to just decode this into a `map[string]interface{}` +structure, read the "type" key, then use something like this library +to decode it into the proper structure. + +## Credits + +Mapstructure was originally created by [@mitchellh](https://github.com/mitchellh). +This is a maintained fork of the original library. + +Read more about the reasons for the fork [here](https://github.com/mitchellh/mapstructure/issues/349). + +## License + +The project is licensed under the [MIT License](LICENSE). diff --git a/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go new file mode 100644 index 00000000..a852a0a0 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go @@ -0,0 +1,714 @@ +package mapstructure + +import ( + "encoding" + "errors" + "fmt" + "net" + "net/netip" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// typedDecodeHook takes a raw DecodeHookFunc (an any) and turns +// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. +func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { + // Create variables here so we can reference them with the reflect pkg + var f1 DecodeHookFuncType + var f2 DecodeHookFuncKind + var f3 DecodeHookFuncValue + + // Fill in the variables into this interface and the rest is done + // automatically using the reflect package. + potential := []any{f1, f2, f3} + + v := reflect.ValueOf(h) + vt := v.Type() + for _, raw := range potential { + pt := reflect.ValueOf(raw).Type() + if vt.ConvertibleTo(pt) { + return v.Convert(pt).Interface() + } + } + + return nil +} + +// cachedDecodeHook takes a raw DecodeHookFunc (an any) and turns +// it into a closure to be used directly +// if the type fails to convert we return a closure always erroring to keep the previous behaviour +func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (any, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from.Type(), to.Type(), from.Interface()) + } + case DecodeHookFuncKind: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from.Kind(), to.Kind(), from.Interface()) + } + case DecodeHookFuncValue: + return func(from reflect.Value, to reflect.Value) (any, error) { + return f(from, to) + } + default: + return func(from reflect.Value, to reflect.Value) (any, error) { + return nil, errors.New("invalid decode hook signature") + } + } +} + +// DecodeHookExec executes the given decode hook. This should be used +// since it'll naturally degrade to the older backwards compatible DecodeHookFunc +// that took reflect.Kind instead of reflect.Type. +func DecodeHookExec( + raw DecodeHookFunc, + from reflect.Value, to reflect.Value, +) (any, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return f(from.Type(), to.Type(), from.Interface()) + case DecodeHookFuncKind: + return f(from.Kind(), to.Kind(), from.Interface()) + case DecodeHookFuncValue: + return f(from, to) + default: + return nil, errors.New("invalid decode hook signature") + } +} + +// ComposeDecodeHookFunc creates a single DecodeHookFunc that +// automatically composes multiple DecodeHookFuncs. +// +// The composed funcs are called in order, with the result of the +// previous transformation. +func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(fs)) + for _, f := range fs { + cached = append(cached, cachedDecodeHook(f)) + } + return func(f reflect.Value, t reflect.Value) (any, error) { + var err error + data := f.Interface() + + newFrom := f + for _, c := range cached { + data, err = c(newFrom, t) + if err != nil { + return nil, err + } + if v, ok := data.(reflect.Value); ok { + newFrom = v + } else { + newFrom = reflect.ValueOf(data) + } + } + + return data, nil + } +} + +// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. +// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. +func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(ff)) + for _, f := range ff { + cached = append(cached, cachedDecodeHook(f)) + } + return func(a, b reflect.Value) (any, error) { + var allErrs string + var out any + var err error + + for _, c := range cached { + out, err = c(a, b) + if err != nil { + allErrs += err.Error() + "\n" + continue + } + + return out, nil + } + + return nil, errors.New(allErrs) + } +} + +// StringToSliceHookFunc returns a DecodeHookFunc that converts +// string to []string by splitting on the given sep. +func StringToSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.SliceOf(f) { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToWeakSliceHookFunc brings back the old (pre-v2) behavior of [StringToSliceHookFunc]. +// +// As of mapstructure v2.0.0 [StringToSliceHookFunc] checks if the return type is a string slice. +// This function removes that check. +func StringToWeakSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Slice { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts +// strings to time.Duration. +func StringToTimeDurationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Duration(5)) { + return data, nil + } + + // Convert it by parsing + d, err := time.ParseDuration(data.(string)) + + return d, wrapTimeParseDurationError(err) + } +} + +// StringToTimeLocationHookFunc returns a DecodeHookFunc that converts +// strings to *time.Location. +func StringToTimeLocationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Local) { + return data, nil + } + d, err := time.LoadLocation(data.(string)) + + return d, wrapTimeParseLocationError(err) + } +} + +// StringToURLHookFunc returns a DecodeHookFunc that converts +// strings to *url.URL. +func StringToURLHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(&url.URL{}) { + return data, nil + } + + // Convert it by parsing + u, err := url.Parse(data.(string)) + + return u, wrapUrlError(err) + } +} + +// StringToIPHookFunc returns a DecodeHookFunc that converts +// strings to net.IP +func StringToIPHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IP{}) { + return data, nil + } + + // Convert it by parsing + ip := net.ParseIP(data.(string)) + if ip == nil { + return net.IP{}, fmt.Errorf("failed parsing ip") + } + + return ip, nil + } +} + +// StringToIPNetHookFunc returns a DecodeHookFunc that converts +// strings to net.IPNet +func StringToIPNetHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IPNet{}) { + return data, nil + } + + // Convert it by parsing + _, net, err := net.ParseCIDR(data.(string)) + return net, wrapNetParseError(err) + } +} + +// StringToTimeHookFunc returns a DecodeHookFunc that converts +// strings to time.Time. +func StringToTimeHookFunc(layout string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Time{}) { + return data, nil + } + + // Convert it by parsing + ti, err := time.Parse(layout, data.(string)) + + return ti, wrapTimeParseError(err) + } +} + +// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to +// the decoder. +// +// Note that this is significantly different from the WeaklyTypedInput option +// of the DecoderConfig. +func WeaklyTypedHook( + f reflect.Kind, + t reflect.Kind, + data any, +) (any, error) { + dataVal := reflect.ValueOf(data) + switch t { + case reflect.String: + switch f { + case reflect.Bool: + if dataVal.Bool() { + return "1", nil + } + return "0", nil + case reflect.Float32: + return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil + case reflect.Int: + return strconv.FormatInt(dataVal.Int(), 10), nil + case reflect.Slice: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + if elemKind == reflect.Uint8 { + return string(dataVal.Interface().([]uint8)), nil + } + case reflect.Uint: + return strconv.FormatUint(dataVal.Uint(), 10), nil + } + } + + return data, nil +} + +func RecursiveStructToMapHookFunc() DecodeHookFunc { + return func(f reflect.Value, t reflect.Value) (any, error) { + if f.Kind() != reflect.Struct { + return f.Interface(), nil + } + + var i any = struct{}{} + if t.Type() != reflect.TypeOf(&i).Elem() { + return f.Interface(), nil + } + + m := make(map[string]any) + t.Set(reflect.ValueOf(m)) + + return f.Interface(), nil + } +} + +// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies +// strings to the UnmarshalText function, when the target type +// implements the encoding.TextUnmarshaler interface +func TextUnmarshallerHookFunc() DecodeHookFuncType { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + result := reflect.New(t).Interface() + unmarshaller, ok := result.(encoding.TextUnmarshaler) + if !ok { + return data, nil + } + str, ok := data.(string) + if !ok { + str = reflect.Indirect(reflect.ValueOf(&data)).Elem().String() + } + if err := unmarshaller.UnmarshalText([]byte(str)); err != nil { + return nil, err + } + return result, nil + } +} + +// StringToNetIPAddrHookFunc returns a DecodeHookFunc that converts +// strings to netip.Addr. +func StringToNetIPAddrHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.Addr{}) { + return data, nil + } + + // Convert it by parsing + addr, err := netip.ParseAddr(data.(string)) + + return addr, wrapNetIPParseAddrError(err) + } +} + +// StringToNetIPAddrPortHookFunc returns a DecodeHookFunc that converts +// strings to netip.AddrPort. +func StringToNetIPAddrPortHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.AddrPort{}) { + return data, nil + } + + // Convert it by parsing + addrPort, err := netip.ParseAddrPort(data.(string)) + + return addrPort, wrapNetIPParseAddrPortError(err) + } +} + +// StringToNetIPPrefixHookFunc returns a DecodeHookFunc that converts +// strings to netip.Prefix. +func StringToNetIPPrefixHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data any, + ) (any, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.Prefix{}) { + return data, nil + } + + // Convert it by parsing + prefix, err := netip.ParsePrefix(data.(string)) + + return prefix, wrapNetIPParsePrefixError(err) + } +} + +// StringToBasicTypeHookFunc returns a DecodeHookFunc that converts +// strings to basic types. +// int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, float32, float64, bool, byte, rune, complex64, complex128 +func StringToBasicTypeHookFunc() DecodeHookFunc { + return ComposeDecodeHookFunc( + StringToInt8HookFunc(), + StringToUint8HookFunc(), + StringToInt16HookFunc(), + StringToUint16HookFunc(), + StringToInt32HookFunc(), + StringToUint32HookFunc(), + StringToInt64HookFunc(), + StringToUint64HookFunc(), + StringToIntHookFunc(), + StringToUintHookFunc(), + StringToFloat32HookFunc(), + StringToFloat64HookFunc(), + StringToBoolHookFunc(), + // byte and rune are aliases for uint8 and int32 respectively + // StringToByteHookFunc(), + // StringToRuneHookFunc(), + StringToComplex64HookFunc(), + StringToComplex128HookFunc(), + ) +} + +// StringToInt8HookFunc returns a DecodeHookFunc that converts +// strings to int8. +func StringToInt8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int8 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 8) + return int8(i64), wrapStrconvNumError(err) + } +} + +// StringToUint8HookFunc returns a DecodeHookFunc that converts +// strings to uint8. +func StringToUint8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint8 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 8) + return uint8(u64), wrapStrconvNumError(err) + } +} + +// StringToInt16HookFunc returns a DecodeHookFunc that converts +// strings to int16. +func StringToInt16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int16 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 16) + return int16(i64), wrapStrconvNumError(err) + } +} + +// StringToUint16HookFunc returns a DecodeHookFunc that converts +// strings to uint16. +func StringToUint16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint16 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 16) + return uint16(u64), wrapStrconvNumError(err) + } +} + +// StringToInt32HookFunc returns a DecodeHookFunc that converts +// strings to int32. +func StringToInt32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int32 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 32) + return int32(i64), wrapStrconvNumError(err) + } +} + +// StringToUint32HookFunc returns a DecodeHookFunc that converts +// strings to uint32. +func StringToUint32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint32 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 32) + return uint32(u64), wrapStrconvNumError(err) + } +} + +// StringToInt64HookFunc returns a DecodeHookFunc that converts +// strings to int64. +func StringToInt64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int64 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 64) + return int64(i64), wrapStrconvNumError(err) + } +} + +// StringToUint64HookFunc returns a DecodeHookFunc that converts +// strings to uint64. +func StringToUint64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 64) + return uint64(u64), wrapStrconvNumError(err) + } +} + +// StringToIntHookFunc returns a DecodeHookFunc that converts +// strings to int. +func StringToIntHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 0) + return int(i64), wrapStrconvNumError(err) + } +} + +// StringToUintHookFunc returns a DecodeHookFunc that converts +// strings to uint. +func StringToUintHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 0) + return uint(u64), wrapStrconvNumError(err) + } +} + +// StringToFloat32HookFunc returns a DecodeHookFunc that converts +// strings to float32. +func StringToFloat32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float32 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 32) + return float32(f64), wrapStrconvNumError(err) + } +} + +// StringToFloat64HookFunc returns a DecodeHookFunc that converts +// strings to float64. +func StringToFloat64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float64 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 64) + return f64, wrapStrconvNumError(err) + } +} + +// StringToBoolHookFunc returns a DecodeHookFunc that converts +// strings to bool. +func StringToBoolHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Bool { + return data, nil + } + + // Convert it by parsing + b, err := strconv.ParseBool(data.(string)) + return b, wrapStrconvNumError(err) + } +} + +// StringToByteHookFunc returns a DecodeHookFunc that converts +// strings to byte. +func StringToByteHookFunc() DecodeHookFunc { + return StringToUint8HookFunc() +} + +// StringToRuneHookFunc returns a DecodeHookFunc that converts +// strings to rune. +func StringToRuneHookFunc() DecodeHookFunc { + return StringToInt32HookFunc() +} + +// StringToComplex64HookFunc returns a DecodeHookFunc that converts +// strings to complex64. +func StringToComplex64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex64 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 64) + return complex64(c128), wrapStrconvNumError(err) + } +} + +// StringToComplex128HookFunc returns a DecodeHookFunc that converts +// strings to complex128. +func StringToComplex128HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 128) + return c128, wrapStrconvNumError(err) + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/errors.go b/vendor/github.com/go-viper/mapstructure/v2/errors.go new file mode 100644 index 00000000..07d31c22 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/errors.go @@ -0,0 +1,244 @@ +package mapstructure + +import ( + "errors" + "fmt" + "net" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// Error interface is implemented by all errors emitted by mapstructure. +// +// Use [errors.As] to check if an error implements this interface. +type Error interface { + error + + mapstructure() +} + +// DecodeError is a generic error type that holds information about +// a decoding error together with the name of the field that caused the error. +type DecodeError struct { + name string + err error +} + +func newDecodeError(name string, err error) *DecodeError { + return &DecodeError{ + name: name, + err: err, + } +} + +func (e *DecodeError) Name() string { + return e.name +} + +func (e *DecodeError) Unwrap() error { + return e.err +} + +func (e *DecodeError) Error() string { + return fmt.Sprintf("'%s' %s", e.name, e.err) +} + +func (*DecodeError) mapstructure() {} + +// ParseError is an error type that indicates a value could not be parsed +// into the expected type. +type ParseError struct { + Expected reflect.Value + Value any + Err error +} + +func (e *ParseError) Error() string { + return fmt.Sprintf("cannot parse value as '%s': %s", e.Expected.Type(), e.Err) +} + +func (*ParseError) mapstructure() {} + +// UnconvertibleTypeError is an error type that indicates a value could not be +// converted to the expected type. +type UnconvertibleTypeError struct { + Expected reflect.Value + Value any +} + +func (e *UnconvertibleTypeError) Error() string { + return fmt.Sprintf( + "expected type '%s', got unconvertible type '%s'", + e.Expected.Type(), + reflect.TypeOf(e.Value), + ) +} + +func (*UnconvertibleTypeError) mapstructure() {} + +func wrapStrconvNumError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*strconv.NumError); ok { + return &strconvNumError{Err: err} + } + + return err +} + +type strconvNumError struct { + Err *strconv.NumError +} + +func (e *strconvNumError) Error() string { + return "strconv." + e.Err.Func + ": " + e.Err.Err.Error() +} + +func (e *strconvNumError) Unwrap() error { return e.Err } + +func wrapUrlError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*url.Error); ok { + return &urlError{Err: err} + } + + return err +} + +type urlError struct { + Err *url.Error +} + +func (e *urlError) Error() string { + return fmt.Sprintf("%s", e.Err.Err) +} + +func (e *urlError) Unwrap() error { return e.Err } + +func wrapNetParseError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*net.ParseError); ok { + return &netParseError{Err: err} + } + + return err +} + +type netParseError struct { + Err *net.ParseError +} + +func (e *netParseError) Error() string { + return "invalid " + e.Err.Type +} + +func (e *netParseError) Unwrap() error { return e.Err } + +func wrapTimeParseError(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(*time.ParseError); ok { + return &timeParseError{Err: err} + } + + return err +} + +type timeParseError struct { + Err *time.ParseError +} + +func (e *timeParseError) Error() string { + if e.Err.Message == "" { + return fmt.Sprintf("parsing time as %q: cannot parse as %q", e.Err.Layout, e.Err.LayoutElem) + } + + return "parsing time " + e.Err.Message +} + +func (e *timeParseError) Unwrap() error { return e.Err } + +func wrapNetIPParseAddrError(err error) error { + if err == nil { + return nil + } + + if errMsg := err.Error(); strings.HasPrefix(errMsg, "ParseAddr") { + errPieces := strings.Split(errMsg, ": ") + + return fmt.Errorf("ParseAddr: %s", errPieces[len(errPieces)-1]) + } + + return err +} + +func wrapNetIPParseAddrPortError(err error) error { + if err == nil { + return nil + } + + errMsg := err.Error() + if strings.HasPrefix(errMsg, "invalid port ") { + return errors.New("invalid port") + } else if strings.HasPrefix(errMsg, "invalid ip:port ") { + return errors.New("invalid ip:port") + } + + return err +} + +func wrapNetIPParsePrefixError(err error) error { + if err == nil { + return nil + } + + if errMsg := err.Error(); strings.HasPrefix(errMsg, "netip.ParsePrefix") { + errPieces := strings.Split(errMsg, ": ") + + return fmt.Errorf("netip.ParsePrefix: %s", errPieces[len(errPieces)-1]) + } + + return err +} + +func wrapTimeParseDurationError(err error) error { + if err == nil { + return nil + } + + errMsg := err.Error() + if strings.HasPrefix(errMsg, "time: unknown unit ") { + return errors.New("time: unknown unit") + } else if strings.HasPrefix(errMsg, "time: ") { + idx := strings.LastIndex(errMsg, " ") + + return errors.New(errMsg[:idx]) + } + + return err +} + +func wrapTimeParseLocationError(err error) error { + if err == nil { + return nil + } + errMsg := err.Error() + if strings.Contains(errMsg, "unknown time zone") || strings.HasPrefix(errMsg, "time: unknown format") { + return fmt.Errorf("invalid time zone format: %w", err) + } + + return err +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock new file mode 100644 index 00000000..5e67bdd6 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.lock @@ -0,0 +1,294 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": [ + "devenv" + ], + "flake-compat": [ + "devenv" + ], + "git-hooks": [ + "devenv" + ], + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1742042642, + "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=", + "owner": "cachix", + "repo": "cachix", + "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "latest", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", + "nix": "nix", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1744876578, + "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=", + "owner": "cachix", + "repo": "devenv", + "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742649964, + "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": [ + "devenv" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": [ + "devenv" + ], + "nixpkgs-regression": [ + "devenv" + ], + "pre-commit-hooks": [ + "devenv" + ] + }, + "locked": { + "lastModified": 1741798497, + "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1733477122, + "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_4" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix new file mode 100644 index 00000000..3b116f42 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + devenv.url = "github:cachix/devenv"; + }; + + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.devenv.flakeModule + ]; + + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + perSystem = + { pkgs, ... }: + rec { + devenv.shells = { + default = { + languages = { + go.enable = true; + }; + + pre-commit.hooks = { + nixpkgs-fmt.enable = true; + }; + + packages = with pkgs; [ + golangci-lint + ]; + + # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 + containers = pkgs.lib.mkForce { }; + }; + + ci = devenv.shells.default; + }; + }; + }; +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go new file mode 100644 index 00000000..d1c15e47 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go @@ -0,0 +1,11 @@ +package errors + +import "errors" + +func New(text string) error { + return errors.New(text) +} + +func As(err error, target interface{}) bool { + return errors.As(err, target) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go new file mode 100644 index 00000000..d74e3a0b --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go @@ -0,0 +1,9 @@ +//go:build go1.20 + +package errors + +import "errors" + +func Join(errs ...error) error { + return errors.Join(errs...) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go new file mode 100644 index 00000000..700b4022 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go @@ -0,0 +1,61 @@ +//go:build !go1.20 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errors + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + // Since Join returns nil if every value in errs is nil, + // e.errs cannot be empty. + if len(e.errs) == 1 { + return e.errs[0].Error() + } + + b := []byte(e.errs[0].Error()) + for _, err := range e.errs[1:] { + b = append(b, '\n') + b = append(b, err.Error()...) + } + // At this point, b has at least one byte '\n'. + // return unsafe.String(&b[0], len(b)) + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go similarity index 74% rename from vendor/github.com/mitchellh/mapstructure/mapstructure.go rename to vendor/github.com/go-viper/mapstructure/v2/mapstructure.go index 1efb22ac..7c35bce0 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -1,5 +1,5 @@ // Package mapstructure exposes functionality to convert one arbitrary -// Go type into another, typically to convert a map[string]interface{} +// Go type into another, typically to convert a map[string]any // into a native Go structure. // // The Go structure can be arbitrarily complex, containing slices, @@ -9,84 +9,84 @@ // // The simplest function to start with is Decode. // -// Field Tags +// # Field Tags // // When decoding to a struct, mapstructure will use the field name by // default to perform the mapping. For example, if a struct has a field // "Username" then mapstructure will look for a key in the source value // of "username" (case insensitive). // -// type User struct { -// Username string -// } +// type User struct { +// Username string +// } // // You can change the behavior of mapstructure by using struct tags. // The default struct tag that mapstructure looks for is "mapstructure" // but you can customize it using DecoderConfig. // -// Renaming Fields +// # Renaming Fields // // To rename the key that mapstructure looks for, use the "mapstructure" // tag and set a value directly. For example, to change the "username" example // above to "user": // -// type User struct { -// Username string `mapstructure:"user"` -// } +// type User struct { +// Username string `mapstructure:"user"` +// } // -// Embedded Structs and Squashing +// # Embedded Structs and Squashing // // Embedded structs are treated as if they're another field with that name. // By default, the two structs below are equivalent when decoding with // mapstructure: // -// type Person struct { -// Name string -// } +// type Person struct { +// Name string +// } // -// type Friend struct { -// Person -// } +// type Friend struct { +// Person +// } // -// type Friend struct { -// Person Person -// } +// type Friend struct { +// Person Person +// } // // This would require an input that looks like below: // -// map[string]interface{}{ -// "person": map[string]interface{}{"name": "alice"}, -// } +// map[string]any{ +// "person": map[string]any{"name": "alice"}, +// } // // If your "person" value is NOT nested, then you can append ",squash" to // your tag value and mapstructure will treat it as if the embedded struct // were part of the struct directly. Example: // -// type Friend struct { -// Person `mapstructure:",squash"` -// } +// type Friend struct { +// Person `mapstructure:",squash"` +// } // // Now the following input would be accepted: // -// map[string]interface{}{ -// "name": "alice", -// } +// map[string]any{ +// "name": "alice", +// } // // When decoding from a struct to a map, the squash tag squashes the struct // fields into a single map. Using the example structs from above: // -// Friend{Person: Person{Name: "alice"}} +// Friend{Person: Person{Name: "alice"}} // // Will be decoded into a map: // -// map[string]interface{}{ -// "name": "alice", -// } +// map[string]any{ +// "name": "alice", +// } // // DecoderConfig has a field that changes the behavior of mapstructure // to always squash embedded structs. // -// Remainder Values +// # Remainder Values // // If there are any unmapped keys in the source value, mapstructure by // default will silently ignore them. You can error by setting ErrorUnused @@ -95,64 +95,85 @@ // // You can also use the ",remain" suffix on your tag to collect all unused // values in a map. The field with this tag MUST be a map type and should -// probably be a "map[string]interface{}" or "map[interface{}]interface{}". +// probably be a "map[string]any" or "map[any]any". // See example below: // -// type Friend struct { -// Name string -// Other map[string]interface{} `mapstructure:",remain"` -// } +// type Friend struct { +// Name string +// Other map[string]any `mapstructure:",remain"` +// } // // Given the input below, Other would be populated with the other // values that weren't used (everything but "name"): // -// map[string]interface{}{ -// "name": "bob", -// "address": "123 Maple St.", -// } +// map[string]any{ +// "name": "bob", +// "address": "123 Maple St.", +// } // -// Omit Empty Values +// # Omit Empty Values // // When decoding from a struct to any other value, you may use the // ",omitempty" suffix on your tag to omit that value if it equates to -// the zero value. The zero value of all types is specified in the Go -// specification. +// the zero value, or a zero-length element. The zero value of all types is +// specified in the Go specification. // // For example, the zero type of a numeric type is zero ("0"). If the struct // field value is zero and a numeric type, the field is empty, and it won't -// be encoded into the destination type. +// be encoded into the destination type. And likewise for the URLs field, if the +// slice is nil or empty, it won't be encoded into the destination type. // -// type Source struct { -// Age int `mapstructure:",omitempty"` -// } +// type Source struct { +// Age int `mapstructure:",omitempty"` +// URLs []string `mapstructure:",omitempty"` +// } // -// Unexported fields +// # Omit Zero Values +// +// When decoding from a struct to any other value, you may use the +// ",omitzero" suffix on your tag to omit that value if it equates to the zero +// value. The zero value of all types is specified in the Go specification. +// +// For example, the zero type of a numeric type is zero ("0"). If the struct +// field value is zero and a numeric type, the field is empty, and it won't +// be encoded into the destination type. And likewise for the URLs field, if the +// slice is nil, it won't be encoded into the destination type. +// +// Note that if the field is a slice, and it is empty but not nil, it will +// still be encoded into the destination type. +// +// type Source struct { +// Age int `mapstructure:",omitzero"` +// URLs []string `mapstructure:",omitzero"` +// } +// +// # Unexported fields // // Since unexported (private) struct fields cannot be set outside the package // where they are defined, the decoder will simply skip them. // // For this output type definition: // -// type Exported struct { -// private string // this unexported field will be skipped -// Public string -// } +// type Exported struct { +// private string // this unexported field will be skipped +// Public string +// } // // Using this map as input: // -// map[string]interface{}{ -// "private": "I will be ignored", -// "Public": "I made it through!", -// } +// map[string]any{ +// "private": "I will be ignored", +// "Public": "I made it through!", +// } // // The following struct will be decoded: // -// type Exported struct { -// private: "" // field is left with an empty string (zero value) -// Public: "I made it through!" -// } +// type Exported struct { +// private: "" // field is left with an empty string (zero value) +// Public: "I made it through!" +// } // -// Other Configuration +// # Other Configuration // // mapstructure is highly configurable. See the DecoderConfig struct // for other features and options that are supported. @@ -160,12 +181,13 @@ package mapstructure import ( "encoding/json" - "errors" "fmt" "reflect" "sort" "strconv" "strings" + + "github.com/go-viper/mapstructure/v2/internal/errors" ) // DecodeHookFunc is the callback function that can be used for @@ -182,19 +204,19 @@ import ( // we started with Kinds and then realized Types were the better solution, // but have a promise to not break backwards compat so we now support // both. -type DecodeHookFunc interface{} +type DecodeHookFunc any // DecodeHookFuncType is a DecodeHookFunc which has complete information about // the source and target types. -type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) +type DecodeHookFuncType func(reflect.Type, reflect.Type, any) (any, error) // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the // source and target types. -type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) +type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error) // DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target // values. -type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error) +type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error) // DecoderConfig is the configuration that is used to create a new decoder // and allows customization of various aspects of decoding. @@ -221,6 +243,12 @@ type DecoderConfig struct { // will affect all nested structs as well. ErrorUnset bool + // AllowUnsetPointer, if set to true, will prevent fields with pointer types + // from being reported as unset, even if ErrorUnset is true and the field was + // not present in the input data. This allows pointer fields to be optional + // without triggering an error when they are missing. + AllowUnsetPointer bool + // ZeroFields, if set to true, will zero fields before writing them. // For example, a map will be emptied before decoded values are put in // it. If this is false, a map will be merged. @@ -259,12 +287,16 @@ type DecoderConfig struct { // Result is a pointer to the struct that will contain the decoded // value. - Result interface{} + Result any // The tag name that mapstructure reads for field names. This // defaults to "mapstructure" TagName string + // The option of the value in the tag that indicates a field should + // be squashed. This defaults to "squash". + SquashTagOption string + // IgnoreUntaggedFields ignores all struct fields without explicit // TagName, comparable to `mapstructure:"-"` as default behaviour. IgnoreUntaggedFields bool @@ -273,6 +305,10 @@ type DecoderConfig struct { // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. MatchName func(mapKey, fieldName string) bool + + // DecodeNil, if set to true, will cause the DecodeHook (if present) to run + // even if the input is nil. This can be used to provide default values. + DecodeNil bool } // A Decoder takes a raw interface value and turns it into structured @@ -282,7 +318,8 @@ type DecoderConfig struct { // structure. The top-level Decode method is just a convenience that sets // up the most basic Decoder. type Decoder struct { - config *DecoderConfig + config *DecoderConfig + cachedDecodeHook func(from reflect.Value, to reflect.Value) (any, error) } // Metadata contains information about decoding a structure that @@ -303,7 +340,7 @@ type Metadata struct { // Decode takes an input structure and uses reflection to translate it to // the output structure. output must be a pointer to a map or struct. -func Decode(input interface{}, output interface{}) error { +func Decode(input any, output any) error { config := &DecoderConfig{ Metadata: nil, Result: output, @@ -319,7 +356,7 @@ func Decode(input interface{}, output interface{}) error { // WeakDecode is the same as Decode but is shorthand to enable // WeaklyTypedInput. See DecoderConfig for more info. -func WeakDecode(input, output interface{}) error { +func WeakDecode(input, output any) error { config := &DecoderConfig{ Metadata: nil, Result: output, @@ -336,7 +373,7 @@ func WeakDecode(input, output interface{}) error { // DecodeMetadata is the same as Decode, but is shorthand to // enable metadata collection. See DecoderConfig for more info. -func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { +func DecodeMetadata(input any, output any, metadata *Metadata) error { config := &DecoderConfig{ Metadata: metadata, Result: output, @@ -353,7 +390,7 @@ func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) e // WeakDecodeMetadata is the same as Decode, but is shorthand to // enable both WeaklyTypedInput and metadata collection. See // DecoderConfig for more info. -func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { +func WeakDecodeMetadata(input any, output any, metadata *Metadata) error { config := &DecoderConfig{ Metadata: metadata, Result: output, @@ -400,6 +437,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { config.TagName = "mapstructure" } + if config.SquashTagOption == "" { + config.SquashTagOption = "squash" + } + if config.MatchName == nil { config.MatchName = strings.EqualFold } @@ -407,29 +448,47 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { result := &Decoder{ config: config, } + if config.DecodeHook != nil { + result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook) + } return result, nil } // Decode decodes the given raw interface to the target pointer specified // by the configuration. -func (d *Decoder) Decode(input interface{}) error { - return d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) -} +func (d *Decoder) Decode(input any) error { + err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) -// Decodes an unknown data type into a specific reflection value. -func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { - var inputVal reflect.Value - if input != nil { - inputVal = reflect.ValueOf(input) + // Retain some of the original behavior when multiple errors ocurr + var joinedErr interface{ Unwrap() []error } + if errors.As(err, &joinedErr) { + return fmt.Errorf("decoding failed due to the following error(s):\n\n%w", err) + } - // We need to check here if input is a typed nil. Typed nils won't - // match the "input == nil" below so we check that here. - if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { - input = nil - } + return err +} + +// isNil returns true if the input is nil or a typed nil pointer. +func isNil(input any) bool { + if input == nil { + return true } + val := reflect.ValueOf(input) + return val.Kind() == reflect.Ptr && val.IsNil() +} +// Decodes an unknown data type into a specific reflection value. +func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { + var ( + inputVal = reflect.ValueOf(input) + outputKind = getKind(outVal) + decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil + ) + if isNil(input) { + // Typed nils won't match the "input == nil" below, so reset input. + input = nil + } if input == nil { // If the data is nil, then we don't set anything, unless ZeroFields is set // to true. @@ -440,30 +499,46 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) } } - return nil + if !decodeNil { + return nil + } } - if !inputVal.IsValid() { - // If the input value is invalid, then we just set the value - // to be the zero value. - outVal.Set(reflect.Zero(outVal.Type())) - if d.config.Metadata != nil && name != "" { - d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + if !decodeNil { + // If the input value is invalid, then we just set the value + // to be the zero value. + outVal.Set(reflect.Zero(outVal.Type())) + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + return nil + } + // Hooks need a valid inputVal, so reset it to zero value of outVal type. + switch outputKind { + case reflect.Struct, reflect.Map: + var mapVal map[string]any + inputVal = reflect.ValueOf(mapVal) // create nil map pointer + case reflect.Slice, reflect.Array: + var sliceVal []any + inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer + default: + inputVal = reflect.Zero(outVal.Type()) } - return nil } - if d.config.DecodeHook != nil { + if d.cachedDecodeHook != nil { // We have a DecodeHook, so let's pre-process the input. var err error - input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal) + input, err = d.cachedDecodeHook(inputVal, outVal) if err != nil { - return fmt.Errorf("error decoding '%s': %s", name, err) + return newDecodeError(name, err) } } + if isNil(input) { + return nil + } var err error - outputKind := getKind(outVal) addMetaKey := true switch outputKind { case reflect.Bool: @@ -478,6 +553,8 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e err = d.decodeUint(name, input, outVal) case reflect.Float32: err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) case reflect.Struct: err = d.decodeStruct(name, input, outVal) case reflect.Map: @@ -492,7 +569,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e err = d.decodeFunc(name, input, outVal) default: // If we reached this point then we weren't able to decode it - return fmt.Errorf("%s: unsupported type: %s", name, outputKind) + return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind)) } // If we reached here, then we successfully decoded SOMETHING, so @@ -506,7 +583,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e // This decodes a basic type (bool, int, string, etc.) and sets the // value to "data" of that type. -func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { if val.IsValid() && val.Elem().IsValid() { elem := val.Elem() @@ -553,16 +630,17 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) dataValType := dataVal.Type() if !dataValType.AssignableTo(val.Type()) { - return fmt.Errorf( - "'%s' expected type '%s', got '%s'", - name, val.Type(), dataValType) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } val.Set(dataVal) return nil } -func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) @@ -606,15 +684,16 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) } if !converted { - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } return nil } -func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -642,26 +721,34 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er if err == nil { val.SetInt(i) } else { - return fmt.Errorf("cannot parse '%s' as int: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := jn.Int64() if err != nil { - return fmt.Errorf( - "error decoding json.Number into %s: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: err, + }) } val.SetInt(i) default: - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } return nil } -func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -670,8 +757,11 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e case dataKind == reflect.Int: i := dataVal.Int() if i < 0 && !d.config.WeaklyTypedInput { - return fmt.Errorf("cannot parse '%s', %d overflows uint", - name, i) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: fmt.Errorf("%d overflows uint", i), + }) } val.SetUint(uint64(i)) case dataKind == reflect.Uint: @@ -679,8 +769,11 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e case dataKind == reflect.Float32: f := dataVal.Float() if f < 0 && !d.config.WeaklyTypedInput { - return fmt.Errorf("cannot parse '%s', %f overflows uint", - name, f) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: fmt.Errorf("%f overflows uint", f), + }) } val.SetUint(uint64(f)) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: @@ -699,26 +792,34 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e if err == nil { val.SetUint(i) } else { - return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := strconv.ParseUint(string(jn), 0, 64) if err != nil { - return fmt.Errorf( - "error decoding json.Number into %s: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) } val.SetUint(i) default: - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } return nil } -func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) @@ -738,18 +839,23 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e } else if dataVal.String() == "" { val.SetBool(false) } else { - return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) } default: - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } return nil } -func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -777,26 +883,51 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) if err == nil { val.SetFloat(f) } else { - return fmt.Errorf("cannot parse '%s' as float: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: wrapStrconvNumError(err), + }) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := jn.Float64() if err != nil { - return fmt.Errorf( - "error decoding json.Number into %s: %s", name, err) + return newDecodeError(name, &ParseError{ + Expected: val, + Value: data, + Err: err, + }) } val.SetFloat(i) default: - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } return nil } -func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Complex64: + val.SetComplex(dataVal.Complex()) + default: + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) + } + + return nil +} + +func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error { valType := val.Type() valKeyType := valType.Key() valElemType := valType.Elem() @@ -811,8 +942,14 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er valMap = reflect.MakeMap(mapType) } + dataVal := reflect.ValueOf(data) + + // Resolve any levels of indirection + for dataVal.Kind() == reflect.Pointer { + dataVal = reflect.Indirect(dataVal) + } + // Check input type and based on the input type jump to the proper func - dataVal := reflect.Indirect(reflect.ValueOf(data)) switch dataVal.Kind() { case reflect.Map: return d.decodeMapFromMap(name, dataVal, val, valMap) @@ -828,7 +965,10 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er fallthrough default: - return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } } @@ -857,7 +997,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle valElemType := valType.Elem() // Accumulate errors - errors := make([]string, 0) + var errs []error // If the input data is empty, then we just match what the input data is. if dataVal.Len() == 0 { @@ -879,7 +1019,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle // First decode the key into the proper type currentKey := reflect.Indirect(reflect.New(valKeyType)) if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) continue } @@ -887,7 +1027,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle v := dataVal.MapIndex(k).Interface() currentVal := reflect.Indirect(reflect.New(valElemType)) if err := d.decode(fieldName, v, currentVal); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) continue } @@ -897,12 +1037,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle // Set the built up map to the value val.Set(valMap) - // If we had errors, return those - if len(errors) > 0 { - return &Error{errors} - } - - return nil + return errors.Join(errs...) } func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { @@ -919,7 +1054,10 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re // to the map value. v := dataVal.Field(i) if !v.Type().AssignableTo(valMap.Type().Elem()) { - return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("cannot assign type %q to map value field of type %q", v.Type(), valMap.Type().Elem()), + ) } tagValue := f.Tag.Get(d.config.TagName) @@ -944,8 +1082,13 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re continue } + // If "omitzero" is specified in the tag, it ignores zero values. + if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() { + continue + } + // If "squash" is specified in the tag, we squash the field down. - squash = squash || strings.Index(tagValue[index+1:], "squash") != -1 + squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption) if squash { // When squashing, the embedded type can be a pointer to a struct. if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { @@ -954,7 +1097,25 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re // The final type must be a struct if v.Kind() != reflect.Struct { - return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("cannot squash non-struct type %q", v.Type()), + ) + } + } else { + if strings.Index(tagValue[index+1:], "remain") != -1 { + if v.Kind() != reflect.Map { + return newDecodeError( + name+"."+f.Name, + fmt.Errorf("error remain-tag field with invalid type: %q", v.Type()), + ) + } + + ptr := v.MapRange() + for ptr.Next() { + valMap.SetMapIndex(ptr.Key(), ptr.Value()) + } + continue } } if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { @@ -1015,7 +1176,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re return nil } -func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) { +func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) { // If the input data is nil, then we want to just set the output // pointer to be nil as well. isNil := data == nil @@ -1062,20 +1223,21 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (b return false, nil } -func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error { // Create an element of the concrete (non pointer) type and decode // into that. Then set the value of the pointer to this type. dataVal := reflect.Indirect(reflect.ValueOf(data)) if val.Type() != dataVal.Type() { - return fmt.Errorf( - "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", - name, val.Type(), dataVal.Type(), data) + return newDecodeError(name, &UnconvertibleTypeError{ + Expected: val, + Value: data, + }) } val.Set(dataVal) return nil } -func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() @@ -1097,7 +1259,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) return nil } // Create slice of maps of other sizes - return d.decodeSlice(name, []interface{}{data}, val) + return d.decodeSlice(name, []any{data}, val) case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: return d.decodeSlice(name, []byte(dataVal.String()), val) @@ -1106,12 +1268,12 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) // and "lift" it into it. i.e. a string becomes a string slice. default: // Just re-try this function with data as a slice. - return d.decodeSlice(name, []interface{}{data}, val) + return d.decodeSlice(name, []any{data}, val) } } - return fmt.Errorf( - "'%s': source data must be an array or slice, got %s", name, dataValKind) + return newDecodeError(name, + fmt.Errorf("source data must be an array or slice, got %s", dataValKind)) } // If the input value is nil, then don't allocate since empty != nil @@ -1123,10 +1285,12 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) if valSlice.IsNil() || d.config.ZeroFields { // Make a new slice to hold our result, same size as the original data. valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } else if valSlice.Len() > dataVal.Len() { + valSlice = valSlice.Slice(0, dataVal.Len()) } // Accumulate any errors - errors := make([]string, 0) + var errs []error for i := 0; i < dataVal.Len(); i++ { currentData := dataVal.Index(i).Interface() @@ -1137,22 +1301,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) fieldName := name + "[" + strconv.Itoa(i) + "]" if err := d.decode(fieldName, currentData, currentField); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) } } // Finally, set the value to the slice we built up val.Set(valSlice) - // If there were errors, we return those - if len(errors) > 0 { - return &Error{errors} - } - - return nil + return errors.Join(errs...) } -func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() @@ -1161,7 +1320,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) valArray := val - if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { + if isComparable(valArray) && valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { // Check input type if dataValKind != reflect.Array && dataValKind != reflect.Slice { if d.config.WeaklyTypedInput { @@ -1177,18 +1336,17 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) // and "lift" it into it. i.e. a string becomes a string array. default: // Just re-try this function with data as a slice. - return d.decodeArray(name, []interface{}{data}, val) + return d.decodeArray(name, []any{data}, val) } } - return fmt.Errorf( - "'%s': source data must be an array or slice, got %s", name, dataValKind) + return newDecodeError(name, + fmt.Errorf("source data must be an array or slice, got %s", dataValKind)) } if dataVal.Len() > arrayType.Len() { - return fmt.Errorf( - "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) - + return newDecodeError(name, + fmt.Errorf("expected source data to have length less or equal to %d, got %d", arrayType.Len(), dataVal.Len())) } // Make a new array to hold our result, same size as the original data. @@ -1196,7 +1354,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) } // Accumulate any errors - errors := make([]string, 0) + var errs []error for i := 0; i < dataVal.Len(); i++ { currentData := dataVal.Index(i).Interface() @@ -1204,22 +1362,17 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) fieldName := name + "[" + strconv.Itoa(i) + "]" if err := d.decode(fieldName, currentData, currentField); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) } } // Finally, set the value to the array we built up val.Set(valArray) - // If there were errors, we return those - if len(errors) > 0 { - return &Error{errors} - } - - return nil + return errors.Join(errs...) } -func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { +func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) // If the type of the value to write to and the data match directly, @@ -1240,7 +1393,7 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // as an intermediary. // Make a new map to hold our result - mapType := reflect.TypeOf((map[string]interface{})(nil)) + mapType := reflect.TypeOf((map[string]any)(nil)) mval := reflect.MakeMap(mapType) // Creating a pointer to a map so that other methods can completely @@ -1258,27 +1411,28 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) return result default: - return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + return newDecodeError(name, + fmt.Errorf("expected a map or struct, got %q", dataValKind)) } } func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { dataValType := dataVal.Type() if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { - return fmt.Errorf( - "'%s' needs a map with string keys, has '%s' keys", - name, dataValType.Key().Kind()) + return newDecodeError(name, + fmt.Errorf("needs a map with string keys, has %q keys", kind)) } dataValKeys := make(map[reflect.Value]struct{}) - dataValKeysUnused := make(map[interface{}]struct{}) + dataValKeysUnused := make(map[any]struct{}) for _, dataValKey := range dataVal.MapKeys() { dataValKeys[dataValKey] = struct{}{} dataValKeysUnused[dataValKey.Interface()] = struct{}{} } - targetValKeysUnused := make(map[interface{}]struct{}) - errors := make([]string, 0) + targetValKeysUnused := make(map[any]struct{}) + + var errs []error // This slice will keep track of all the structs we'll be decoding. // There can be more than one struct if there are embedded structs @@ -1319,7 +1473,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e // We always parse the tags cause we're looking for other tags too tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") for _, tag := range tagParts[1:] { - if tag == "squash" { + if tag == d.config.SquashTagOption { squash = true break } @@ -1331,11 +1485,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } if squash { - if fieldVal.Kind() != reflect.Struct { - errors = appendErrors(errors, - fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) - } else { + switch fieldVal.Kind() { + case reflect.Struct: structs = append(structs, fieldVal) + case reflect.Interface: + if !fieldVal.IsNil() { + structs = append(structs, fieldVal.Elem().Elem()) + } + default: + errs = append(errs, newDecodeError( + name+"."+fieldType.Name, + fmt.Errorf("unsupported type for squash: %s", fieldVal.Kind()), + )) } continue } @@ -1356,6 +1517,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e fieldName := field.Name tagValue := field.Tag.Get(d.config.TagName) + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue @@ -1383,7 +1547,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e if !rawMapVal.IsValid() { // There was no matching key in the map for the value in // the struct. Remember it for potential errors and metadata. - targetValKeysUnused[fieldName] = struct{}{} + if !(d.config.AllowUnsetPointer && fieldValue.Kind() == reflect.Ptr) { + targetValKeysUnused[fieldName] = struct{}{} + } continue } } @@ -1409,7 +1575,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) } } @@ -1417,14 +1583,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e // we put the unused keys directly into the remain field. if remainField != nil && len(dataValKeysUnused) > 0 { // Build a map of only the unused values - remain := map[interface{}]interface{}{} + remain := map[any]any{} for key := range dataValKeysUnused { remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() } // Decode it as-if we were just decoding this map onto our map. if err := d.decodeMap(name, remain, remainField.val); err != nil { - errors = appendErrors(errors, err) + errs = append(errs, err) } // Set the map to nil so we have none so that the next check will @@ -1439,8 +1605,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } sort.Strings(keys) - err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) - errors = appendErrors(errors, err) + errs = append(errs, newDecodeError( + name, + fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")), + )) } if d.config.ErrorUnset && len(targetValKeysUnused) > 0 { @@ -1450,12 +1618,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } sort.Strings(keys) - err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", ")) - errors = appendErrors(errors, err) + errs = append(errs, newDecodeError( + name, + fmt.Errorf("has unset fields: %s", strings.Join(keys, ", ")), + )) } - if len(errors) > 0 { - return &Error{errors} + if err := errors.Join(errs...); err != nil { + return err } // Add the unused keys to the list of unused keys if we're tracking metadata @@ -1509,6 +1679,8 @@ func getKind(val reflect.Value) reflect.Kind { return reflect.Uint case kind >= reflect.Float32 && kind <= reflect.Float64: return reflect.Float32 + case kind >= reflect.Complex64 && kind <= reflect.Complex128: + return reflect.Complex64 default: return kind } diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go new file mode 100644 index 00000000..d0913fff --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go @@ -0,0 +1,44 @@ +//go:build !go1.20 + +package mapstructure + +import "reflect" + +func isComparable(v reflect.Value) bool { + k := v.Kind() + switch k { + case reflect.Invalid: + return false + + case reflect.Array: + switch v.Type().Elem().Kind() { + case reflect.Interface, reflect.Array, reflect.Struct: + for i := 0; i < v.Type().Len(); i++ { + // if !v.Index(i).Comparable() { + if !isComparable(v.Index(i)) { + return false + } + } + return true + } + return v.Type().Comparable() + + case reflect.Interface: + // return v.Elem().Comparable() + return isComparable(v.Elem()) + + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + return false + + // if !v.Field(i).Comparable() { + if !isComparable(v.Field(i)) { + return false + } + } + return true + + default: + return v.Type().Comparable() + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go new file mode 100644 index 00000000..f8255a1b --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go @@ -0,0 +1,10 @@ +//go:build go1.20 + +package mapstructure + +import "reflect" + +// TODO: remove once we drop support for Go <1.20 +func isComparable(v reflect.Value) bool { + return v.Comparable() +} diff --git a/vendor/github.com/hashicorp/consul/api/acl.go b/vendor/github.com/hashicorp/consul/api/acl.go index b0ec6f2f..1a13fd38 100644 --- a/vendor/github.com/hashicorp/consul/api/acl.go +++ b/vendor/github.com/hashicorp/consul/api/acl.go @@ -10,7 +10,7 @@ import ( "net/url" "time" - "github.com/mitchellh/mapstructure" + "github.com/go-viper/mapstructure/v2" ) const ( diff --git a/vendor/github.com/hashicorp/consul/api/api.go b/vendor/github.com/hashicorp/consul/api/api.go index 27af1ea5..31319390 100644 --- a/vendor/github.com/hashicorp/consul/api/api.go +++ b/vendor/github.com/hashicorp/consul/api/api.go @@ -142,7 +142,7 @@ type QueryOptions struct { RequireConsistent bool // UseCache requests that the agent cache results locally. See - // https://www.consul.io/api/features/caching.html for more details on the + // https://developer.hashicorp.com/api/features/caching.html for more details on the // semantics. UseCache bool @@ -152,14 +152,14 @@ type QueryOptions struct { // returned. Clients that wish to allow for stale results on error can set // StaleIfError to a longer duration to change this behavior. It is ignored // if the endpoint supports background refresh caching. See - // https://www.consul.io/api/features/caching.html for more details. + // https://developer.hashicorp.com/api/features/caching.html for more details. MaxAge time.Duration // StaleIfError specifies how stale the client will accept a cached response // if the servers are unavailable to fetch a fresh one. Only makes sense when // UseCache is true and MaxAge is set to a lower, non-zero value. It is // ignored if the endpoint supports background refresh caching. See - // https://www.consul.io/api/features/caching.html for more details. + // https://developer.hashicorp.com/api/features/caching.html for more details. StaleIfError time.Duration // WaitIndex is used to enable a blocking query. Waits @@ -646,9 +646,7 @@ func (c *Client) Headers() http.Header { ret := make(http.Header) for k, v := range c.headers { - for _, val := range v { - ret[k] = append(ret[k], val) - } + ret[k] = append(ret[k], v...) } return ret @@ -1033,12 +1031,11 @@ func (r *request) toHTTP() (*http.Request, error) { req.Header = r.header // Content-Type must always be set when a body is present - // See https://github.com/hashicorp/consul/issues/10011 if req.Body != nil && req.Header.Get("Content-Type") == "" { - req.Header.Set("Content-Type", "application/json") + req.Header.Set("Content-Type", "application/json; charset=utf-8") } - // Setup auth + // Check for a token if r.config.HttpAuth != nil { req.SetBasicAuth(r.config.HttpAuth.Username, r.config.HttpAuth.Password) } @@ -1313,7 +1310,7 @@ func generateUnexpectedResponseCodeError(resp *http.Response) error { io.Copy(&buf, resp.Body) closeResponseBody(resp) - trimmed := strings.TrimSpace(string(buf.Bytes())) + trimmed := strings.TrimSpace(buf.String()) return StatusError{Code: resp.StatusCode, Body: trimmed} } diff --git a/vendor/github.com/hashicorp/consul/api/config_entry.go b/vendor/github.com/hashicorp/consul/api/config_entry.go index 8c3a080f..3dde8d90 100644 --- a/vendor/github.com/hashicorp/consul/api/config_entry.go +++ b/vendor/github.com/hashicorp/consul/api/config_entry.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/mitchellh/mapstructure" + "github.com/go-viper/mapstructure/v2" ) const ( @@ -381,6 +381,7 @@ type ServiceConfigEntry struct { Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 + MaxRequestHeadersKB *uint32 `json:",omitempty"` } func (s *ServiceConfigEntry) GetKind() string { return s.Kind } diff --git a/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go b/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go index ba2bac19..76acfbf7 100644 --- a/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go +++ b/vendor/github.com/hashicorp/consul/api/config_entry_gateways.go @@ -291,6 +291,10 @@ type APIGatewayListener struct { Override *APIGatewayPolicy `json:",omitempty"` // Default is the policy that is the default for the listener and route, routes can override this behavior Default *APIGatewayPolicy `json:",omitempty"` + + // MaxRequestHeadersKB configures the maximum size in kilobytes for request headers + // sent from downstream clients to upstream services. If not set, uses Envoy's default. + MaxRequestHeadersKB *uint32 `json:",omitempty"` } // APIGatewayTLSConfiguration specifies the configuration of a listener’s diff --git a/vendor/github.com/hashicorp/consul/api/connect_ca.go b/vendor/github.com/hashicorp/consul/api/connect_ca.go index 8a5c9f87..d2d276ca 100644 --- a/vendor/github.com/hashicorp/consul/api/connect_ca.go +++ b/vendor/github.com/hashicorp/consul/api/connect_ca.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "github.com/mitchellh/mapstructure" + "github.com/go-viper/mapstructure/v2" ) // CAConfig is the structure for the Connect CA configuration. diff --git a/vendor/github.com/hashicorp/consul/api/content_type.go b/vendor/github.com/hashicorp/consul/api/content_type.go index 37c8cf60..7aef2c30 100644 --- a/vendor/github.com/hashicorp/consul/api/content_type.go +++ b/vendor/github.com/hashicorp/consul/api/content_type.go @@ -12,7 +12,7 @@ const ( contentTypeHeader = "Content-Type" plainContentType = "text/plain; charset=utf-8" octetStream = "application/octet-stream" - jsonContentType = "application/json" // Default content type + jsonContentType = "application/json; charset=utf-8" // Default content type ) // ContentTypeRule defines a rule for determining the content type of an HTTP request. diff --git a/vendor/github.com/hashicorp/consul/api/lock.go b/vendor/github.com/hashicorp/consul/api/lock.go index e9529f7b..a3885a76 100644 --- a/vendor/github.com/hashicorp/consul/api/lock.go +++ b/vendor/github.com/hashicorp/consul/api/lock.go @@ -59,7 +59,7 @@ var ( ) // Lock is used to implement client-side leader election. It is follows the -// algorithm as described here: https://www.consul.io/docs/guides/leader-election.html. +// algorithm as described here: https://developer.hashicorp.com/docs/guides/leader-election.html. type Lock struct { c *Client opts *LockOptions diff --git a/vendor/github.com/hashicorp/consul/api/namespace.go b/vendor/github.com/hashicorp/consul/api/namespace.go index 98afd229..ac189a8f 100644 --- a/vendor/github.com/hashicorp/consul/api/namespace.go +++ b/vendor/github.com/hashicorp/consul/api/namespace.go @@ -85,14 +85,10 @@ func (n *NamespaceACLConfig) UnmarshalJSON(data []byte) error { } if n.PolicyDefaults == nil { - for _, pd := range aux.PolicyDefaultsSnake { - n.PolicyDefaults = append(n.PolicyDefaults, pd) - } + n.PolicyDefaults = append(n.PolicyDefaults, aux.PolicyDefaultsSnake...) } if n.RoleDefaults == nil { - for _, pd := range aux.RoleDefaultsSnake { - n.RoleDefaults = append(n.RoleDefaults, pd) - } + n.RoleDefaults = append(n.RoleDefaults, aux.RoleDefaultsSnake...) } return nil } diff --git a/vendor/github.com/hashicorp/consul/api/operator_autopilot.go b/vendor/github.com/hashicorp/consul/api/operator_autopilot.go index 7628bf6f..20fc9344 100644 --- a/vendor/github.com/hashicorp/consul/api/operator_autopilot.go +++ b/vendor/github.com/hashicorp/consul/api/operator_autopilot.go @@ -62,7 +62,7 @@ type AutopilotConfiguration struct { } // Defines default values for the AutopilotConfiguration type, consistent with -// https://www.consul.io/api-docs/operator/autopilot#parameters-1 +// https://developer.hashicorp.com/api-docs/operator/autopilot#parameters-1 func NewAutopilotConfiguration() AutopilotConfiguration { cfg := AutopilotConfiguration{ CleanupDeadServers: true, diff --git a/vendor/github.com/hashicorp/consul/api/operator_utilization.go b/vendor/github.com/hashicorp/consul/api/operator_utilization.go new file mode 100644 index 00000000..d1dd4bbd --- /dev/null +++ b/vendor/github.com/hashicorp/consul/api/operator_utilization.go @@ -0,0 +1,64 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package api + +import ( + "io" + "strconv" +) + +type Census struct { + client *Client +} + +// UtilizationBundleRequest configures generation of a census utilization bundle. +type UtilizationBundleRequest struct { + Message string + TodayOnly bool + SendReport bool +} + +func (c *Client) Census() *Census { + return &Census{c} +} + +// Utilization generates a census utilization bundle by calling the +// /v1/operator/utilization endpoint. The returned byte slice contains the bundle +// JSON payload suitable for saving to disk or further processing. +func (c *Census) Utilization(req *UtilizationBundleRequest, q *QueryOptions) ([]byte, *QueryMeta, error) { + r := c.client.newRequest("GET", "/v1/operator/utilization") + if q != nil { + r.setQueryOptions(q) + } + if req != nil { + if req.Message != "" { + r.params.Set("message", req.Message) + } + if req.TodayOnly { + r.params.Set("today_only", strconv.FormatBool(true)) + } + if req.SendReport { + r.params.Set("send_report", strconv.FormatBool(true)) + } + } + + rtt, resp, err := c.client.doRequest(r) + if err != nil { + return nil, nil, err + } + defer closeResponseBody(resp) + if err := requireOK(resp); err != nil { + return nil, nil, err + } + + qm := &QueryMeta{} + parseQueryMeta(resp, qm) + qm.RequestTime = rtt + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, nil, err + } + return body, qm, nil +} diff --git a/vendor/github.com/hashicorp/consul/api/peering.go b/vendor/github.com/hashicorp/consul/api/peering.go index dd7780f6..32ed28ed 100644 --- a/vendor/github.com/hashicorp/consul/api/peering.go +++ b/vendor/github.com/hashicorp/consul/api/peering.go @@ -218,7 +218,7 @@ func (p *Peerings) GenerateToken(ctx context.Context, g PeeringGenerateTokenRequ return nil, nil, fmt.Errorf("peer name cannot be empty") } - req := p.c.newRequest("POST", fmt.Sprint("/v1/peering/token")) + req := p.c.newRequest("POST", "/v1/peering/token") req.setWriteOptions(wq) req.ctx = ctx req.obj = g @@ -244,7 +244,7 @@ func (p *Peerings) GenerateToken(ctx context.Context, g PeeringGenerateTokenRequ // TODO(peering): verify this is the ultimate signature we want func (p *Peerings) Establish(ctx context.Context, i PeeringEstablishRequest, wq *WriteOptions) (*PeeringEstablishResponse, *WriteMeta, error) { - req := p.c.newRequest("POST", fmt.Sprint("/v1/peering/establish")) + req := p.c.newRequest("POST", "/v1/peering/establish") req.setWriteOptions(wq) req.ctx = ctx req.obj = i diff --git a/vendor/github.com/hashicorp/consul/api/txn.go b/vendor/github.com/hashicorp/consul/api/txn.go index 65b6e883..5f43c490 100644 --- a/vendor/github.com/hashicorp/consul/api/txn.go +++ b/vendor/github.com/hashicorp/consul/api/txn.go @@ -36,7 +36,7 @@ type TxnOps []*TxnOp type TxnResult struct { KV *KVPair Node *Node - Service *AgentService + Service *CatalogService Check *HealthCheck } @@ -164,6 +164,13 @@ type CheckTxnOp struct { Check HealthCheck } +type CensusOp string + +const ( + CensusPut CensusOp = "put" + CensusPrune CensusOp = "prune" +) + // Txn is used to apply multiple Consul operations in a single, atomic transaction. // // Note that Go will perform the required base64 encoding on the values diff --git a/vendor/github.com/hashicorp/consul/api/watch/watch.go b/vendor/github.com/hashicorp/consul/api/watch/watch.go index ea00f8ef..b770735a 100644 --- a/vendor/github.com/hashicorp/consul/api/watch/watch.go +++ b/vendor/github.com/hashicorp/consul/api/watch/watch.go @@ -5,14 +5,15 @@ package watch import ( "context" + "errors" "fmt" "io" "sync" "time" + "github.com/go-viper/mapstructure/v2" consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" - "github.com/mitchellh/mapstructure" ) const DefaultTimeout = 10 * time.Second @@ -166,11 +167,11 @@ func ParseExempt(params map[string]interface{}, exempt []string) (*Plan, error) switch plan.HandlerType { case "http": if _, ok := params["http_handler_config"]; !ok { - return nil, fmt.Errorf("Handler type 'http' requires 'http_handler_config' to be set") + return nil, errors.New("Handler type 'http' requires 'http_handler_config' to be set") } config, err := parseHttpHandlerConfig(params["http_handler_config"]) if err != nil { - return nil, fmt.Errorf(fmt.Sprintf("Failed to parse 'http_handler_config': %v", err)) + return nil, fmt.Errorf("Failed to parse 'http_handler_config': %v", err) } plan.Exempt["http_handler_config"] = config delete(params, "http_handler_config") @@ -244,18 +245,18 @@ func assignValueBool(params map[string]interface{}, name string, out *bool) erro func assignValueStringSlice(params map[string]interface{}, name string, out *[]string) error { if raw, ok := params[name]; ok { var tmp []string - switch raw.(type) { + switch raw := raw.(type) { case string: - tmp = make([]string, 1, 1) - tmp[0] = raw.(string) + tmp = make([]string, 1) + tmp[0] = raw case []string: - l := len(raw.([]string)) - tmp = make([]string, l, l) - copy(tmp, raw.([]string)) + l := len(raw) + tmp = make([]string, l) + copy(tmp, raw) case []interface{}: - l := len(raw.([]interface{})) - tmp = make([]string, l, l) - for i, v := range raw.([]interface{}) { + l := len(raw) + tmp = make([]string, l) + for i, v := range raw { if s, ok := v.(string); ok { tmp[i] = s } else { @@ -287,7 +288,7 @@ func parseHttpHandlerConfig(configParams interface{}) (*HttpHandlerConfig, error if config.TimeoutRaw == "" { config.Timeout = DefaultTimeout } else if timeout, err := time.ParseDuration(config.TimeoutRaw); err != nil { - return nil, fmt.Errorf(fmt.Sprintf("Failed to parse timeout: %v", err)) + return nil, fmt.Errorf("Failed to parse timeout: %v", err) } else { config.Timeout = timeout } diff --git a/vendor/github.com/mitchellh/mapstructure/README.md b/vendor/github.com/mitchellh/mapstructure/README.md deleted file mode 100644 index 0018dc7d..00000000 --- a/vendor/github.com/mitchellh/mapstructure/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure) - -mapstructure is a Go library for decoding generic map values to structures -and vice versa, while providing helpful error handling. - -This library is most useful when decoding values from some data stream (JSON, -Gob, etc.) where you don't _quite_ know the structure of the underlying data -until you read a part of it. You can therefore read a `map[string]interface{}` -and use this library to decode it into the proper underlying native Go -structure. - -## Installation - -Standard `go get`: - -``` -$ go get github.com/mitchellh/mapstructure -``` - -## Usage & Example - -For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure). - -The `Decode` function has examples associated with it there. - -## But Why?! - -Go offers fantastic standard libraries for decoding formats such as JSON. -The standard method is to have a struct pre-created, and populate that struct -from the bytes of the encoded format. This is great, but the problem is if -you have configuration or an encoding that changes slightly depending on -specific fields. For example, consider this JSON: - -```json -{ - "type": "person", - "name": "Mitchell" -} -``` - -Perhaps we can't populate a specific structure without first reading -the "type" field from the JSON. We could always do two passes over the -decoding of the JSON (reading the "type" first, and the rest later). -However, it is much simpler to just decode this into a `map[string]interface{}` -structure, read the "type" key, then use something like this library -to decode it into the proper structure. diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go deleted file mode 100644 index 3a754ca7..00000000 --- a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go +++ /dev/null @@ -1,279 +0,0 @@ -package mapstructure - -import ( - "encoding" - "errors" - "fmt" - "net" - "reflect" - "strconv" - "strings" - "time" -) - -// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns -// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. -func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { - // Create variables here so we can reference them with the reflect pkg - var f1 DecodeHookFuncType - var f2 DecodeHookFuncKind - var f3 DecodeHookFuncValue - - // Fill in the variables into this interface and the rest is done - // automatically using the reflect package. - potential := []interface{}{f1, f2, f3} - - v := reflect.ValueOf(h) - vt := v.Type() - for _, raw := range potential { - pt := reflect.ValueOf(raw).Type() - if vt.ConvertibleTo(pt) { - return v.Convert(pt).Interface() - } - } - - return nil -} - -// DecodeHookExec executes the given decode hook. This should be used -// since it'll naturally degrade to the older backwards compatible DecodeHookFunc -// that took reflect.Kind instead of reflect.Type. -func DecodeHookExec( - raw DecodeHookFunc, - from reflect.Value, to reflect.Value) (interface{}, error) { - - switch f := typedDecodeHook(raw).(type) { - case DecodeHookFuncType: - return f(from.Type(), to.Type(), from.Interface()) - case DecodeHookFuncKind: - return f(from.Kind(), to.Kind(), from.Interface()) - case DecodeHookFuncValue: - return f(from, to) - default: - return nil, errors.New("invalid decode hook signature") - } -} - -// ComposeDecodeHookFunc creates a single DecodeHookFunc that -// automatically composes multiple DecodeHookFuncs. -// -// The composed funcs are called in order, with the result of the -// previous transformation. -func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { - return func(f reflect.Value, t reflect.Value) (interface{}, error) { - var err error - data := f.Interface() - - newFrom := f - for _, f1 := range fs { - data, err = DecodeHookExec(f1, newFrom, t) - if err != nil { - return nil, err - } - newFrom = reflect.ValueOf(data) - } - - return data, nil - } -} - -// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. -// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. -func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { - return func(a, b reflect.Value) (interface{}, error) { - var allErrs string - var out interface{} - var err error - - for _, f := range ff { - out, err = DecodeHookExec(f, a, b) - if err != nil { - allErrs += err.Error() + "\n" - continue - } - - return out, nil - } - - return nil, errors.New(allErrs) - } -} - -// StringToSliceHookFunc returns a DecodeHookFunc that converts -// string to []string by splitting on the given sep. -func StringToSliceHookFunc(sep string) DecodeHookFunc { - return func( - f reflect.Kind, - t reflect.Kind, - data interface{}) (interface{}, error) { - if f != reflect.String || t != reflect.Slice { - return data, nil - } - - raw := data.(string) - if raw == "" { - return []string{}, nil - } - - return strings.Split(raw, sep), nil - } -} - -// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts -// strings to time.Duration. -func StringToTimeDurationHookFunc() DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(time.Duration(5)) { - return data, nil - } - - // Convert it by parsing - return time.ParseDuration(data.(string)) - } -} - -// StringToIPHookFunc returns a DecodeHookFunc that converts -// strings to net.IP -func StringToIPHookFunc() DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(net.IP{}) { - return data, nil - } - - // Convert it by parsing - ip := net.ParseIP(data.(string)) - if ip == nil { - return net.IP{}, fmt.Errorf("failed parsing ip %v", data) - } - - return ip, nil - } -} - -// StringToIPNetHookFunc returns a DecodeHookFunc that converts -// strings to net.IPNet -func StringToIPNetHookFunc() DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(net.IPNet{}) { - return data, nil - } - - // Convert it by parsing - _, net, err := net.ParseCIDR(data.(string)) - return net, err - } -} - -// StringToTimeHookFunc returns a DecodeHookFunc that converts -// strings to time.Time. -func StringToTimeHookFunc(layout string) DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(time.Time{}) { - return data, nil - } - - // Convert it by parsing - return time.Parse(layout, data.(string)) - } -} - -// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to -// the decoder. -// -// Note that this is significantly different from the WeaklyTypedInput option -// of the DecoderConfig. -func WeaklyTypedHook( - f reflect.Kind, - t reflect.Kind, - data interface{}) (interface{}, error) { - dataVal := reflect.ValueOf(data) - switch t { - case reflect.String: - switch f { - case reflect.Bool: - if dataVal.Bool() { - return "1", nil - } - return "0", nil - case reflect.Float32: - return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil - case reflect.Int: - return strconv.FormatInt(dataVal.Int(), 10), nil - case reflect.Slice: - dataType := dataVal.Type() - elemKind := dataType.Elem().Kind() - if elemKind == reflect.Uint8 { - return string(dataVal.Interface().([]uint8)), nil - } - case reflect.Uint: - return strconv.FormatUint(dataVal.Uint(), 10), nil - } - } - - return data, nil -} - -func RecursiveStructToMapHookFunc() DecodeHookFunc { - return func(f reflect.Value, t reflect.Value) (interface{}, error) { - if f.Kind() != reflect.Struct { - return f.Interface(), nil - } - - var i interface{} = struct{}{} - if t.Type() != reflect.TypeOf(&i).Elem() { - return f.Interface(), nil - } - - m := make(map[string]interface{}) - t.Set(reflect.ValueOf(m)) - - return f.Interface(), nil - } -} - -// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies -// strings to the UnmarshalText function, when the target type -// implements the encoding.TextUnmarshaler interface -func TextUnmarshallerHookFunc() DecodeHookFuncType { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - result := reflect.New(t).Interface() - unmarshaller, ok := result.(encoding.TextUnmarshaler) - if !ok { - return data, nil - } - if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil { - return nil, err - } - return result, nil - } -} diff --git a/vendor/github.com/mitchellh/mapstructure/error.go b/vendor/github.com/mitchellh/mapstructure/error.go deleted file mode 100644 index 47a99e5a..00000000 --- a/vendor/github.com/mitchellh/mapstructure/error.go +++ /dev/null @@ -1,50 +0,0 @@ -package mapstructure - -import ( - "errors" - "fmt" - "sort" - "strings" -) - -// Error implements the error interface and can represents multiple -// errors that occur in the course of a single decode. -type Error struct { - Errors []string -} - -func (e *Error) Error() string { - points := make([]string, len(e.Errors)) - for i, err := range e.Errors { - points[i] = fmt.Sprintf("* %s", err) - } - - sort.Strings(points) - return fmt.Sprintf( - "%d error(s) decoding:\n\n%s", - len(e.Errors), strings.Join(points, "\n")) -} - -// WrappedErrors implements the errwrap.Wrapper interface to make this -// return value more useful with the errwrap and go-multierror libraries. -func (e *Error) WrappedErrors() []error { - if e == nil { - return nil - } - - result := make([]error, len(e.Errors)) - for i, e := range e.Errors { - result[i] = errors.New(e) - } - - return result -} - -func appendErrors(errors []string, err error) []string { - switch e := err.(type) { - case *Error: - return append(errors, e.Errors...) - default: - return append(errors, e.Error()) - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 29dd9c73..5b0c09fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -13,8 +13,12 @@ github.com/dgryski/go-rendezvous # github.com/fatih/color v1.18.0 ## explicit; go 1.17 github.com/fatih/color -# github.com/hashicorp/consul/api v1.32.1 -## explicit; go 1.23.8 +# github.com/go-viper/mapstructure/v2 v2.4.0 +## explicit; go 1.18 +github.com/go-viper/mapstructure/v2 +github.com/go-viper/mapstructure/v2/internal/errors +# github.com/hashicorp/consul/api v1.32.4 +## explicit; go 1.25.0 github.com/hashicorp/consul/api github.com/hashicorp/consul/api/watch # github.com/hashicorp/errwrap v1.1.0 @@ -45,8 +49,6 @@ github.com/hashicorp/golang-lru/simplelru # github.com/hashicorp/serf v0.10.2 ## explicit; go 1.19 github.com/hashicorp/serf/coordinate -# github.com/kr/text v0.2.0 -## explicit # github.com/mattn/go-colorable v0.1.14 ## explicit; go 1.18 github.com/mattn/go-colorable @@ -56,9 +58,6 @@ github.com/mattn/go-isatty # github.com/mitchellh/go-homedir v1.1.0 ## explicit github.com/mitchellh/go-homedir -# github.com/mitchellh/mapstructure v1.5.0 -## explicit; go 1.14 -github.com/mitchellh/mapstructure # github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e ## explicit; go 1.12 # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2