mirror of https://github.com/docker/cli.git
vendor: migrate to github.com/go-viper/mapstructure/v2 v2.0.0
github.com/mitchellh/mapstructure will no longer be maintained by the author, and github.com/go-viper/mapstructure is nominated as the endorsed fork. - v1.x changes since last release from mitchellh: https://github.com/go-viper/mapstructure/compare/v1.5.0...v1.6.0 - v2.0 changes: https://github.com/go-viper/mapstructure/compare/v1.6.0...v2.0.0 Breaking changes Error is removed in favor of errors.Join (backported from Go 1.20 to preserve compatibility with earlier versions) What's Changed - feat!: update module path - build: update dev env - feature: add StringToBasicTypeHookFunc and support complex - Add an example showing how to use a DecodeHookFunc to parse a custom field. - Remove exposed error type - Replace internal joined error with errors.Join Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
6b5ebfb4f7
commit
bc2e274782
|
@ -21,8 +21,8 @@ import (
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
|
"github.com/go-viper/mapstructure/v2"
|
||||||
"github.com/google/shlex"
|
"github.com/google/shlex"
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
|
@ -17,11 +17,11 @@ require (
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/fvbommel/sortorder v1.0.2
|
github.com/fvbommel/sortorder v1.0.2
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.0.0
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/mattn/go-runewidth v0.0.15
|
github.com/mattn/go-runewidth v0.0.15
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
|
||||||
github.com/moby/patternmatcher v0.6.0
|
github.com/moby/patternmatcher v0.6.0
|
||||||
github.com/moby/swarmkit/v2 v2.0.0-20240415162501-c1c857e2dca1
|
github.com/moby/swarmkit/v2 v2.0.0-20240415162501-c1c857e2dca1
|
||||||
github.com/moby/sys/sequential v0.5.0
|
github.com/moby/sys/sequential v0.5.0
|
||||||
|
|
|
@ -96,6 +96,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
@ -171,9 +173,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr
|
||||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
|
github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
|
||||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
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/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
||||||
|
/.devenv/
|
||||||
|
/.direnv/
|
||||||
|
/.pre-commit-config.yaml
|
||||||
|
/bin/
|
||||||
|
/build/
|
||||||
|
/var/
|
|
@ -0,0 +1,23 @@
|
||||||
|
run:
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gci:
|
||||||
|
sections:
|
||||||
|
- standard
|
||||||
|
- default
|
||||||
|
- prefix(github.com/go-viper/mapstructure)
|
||||||
|
golint:
|
||||||
|
min-confidence: 0
|
||||||
|
goimports:
|
||||||
|
local-prefixes: github.com/go-viper/maptstructure
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- gci
|
||||||
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
- goimports
|
||||||
|
- staticcheck
|
||||||
|
# - stylecheck
|
|
@ -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
|
## 1.5.0
|
||||||
|
|
||||||
* New option `IgnoreUntaggedFields` to ignore decoding to any fields
|
* New option `IgnoreUntaggedFields` to ignore decoding to any fields
|
|
@ -0,0 +1,80 @@
|
||||||
|
# mapstructure
|
||||||
|
|
||||||
|
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/go-viper/mapstructure/ci.yaml?branch=main&style=flat-square)](https://github.com/go-viper/mapstructure/actions?query=workflow%3ACI)
|
||||||
|
[![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)
|
||||||
|
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.18-61CFDD.svg?style=flat-square)
|
||||||
|
|
||||||
|
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).
|
|
@ -0,0 +1,577 @@
|
||||||
|
package mapstructure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"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.Type,
|
||||||
|
t reflect.Type,
|
||||||
|
data interface{},
|
||||||
|
) (interface{}, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
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 interface{},
|
||||||
|
) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if t != reflect.TypeOf(netip.Addr{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return netip.ParseAddr(data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToNetIPAddrPortHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to netip.AddrPort.
|
||||||
|
func StringToNetIPAddrPortHookFunc() 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(netip.AddrPort{}) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return netip.ParseAddrPort(data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUint8HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to uint8.
|
||||||
|
func StringToUint8HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToInt16HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to int16.
|
||||||
|
func StringToInt16HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUint16HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to uint16.
|
||||||
|
func StringToUint16HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToInt32HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to int32.
|
||||||
|
func StringToInt32HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUint32HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to uint32.
|
||||||
|
func StringToUint32HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToInt64HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to int64.
|
||||||
|
func StringToInt64HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String || t.Kind() != reflect.Int64 {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return strconv.ParseInt(data.(string), 0, 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUint64HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to uint64.
|
||||||
|
func StringToUint64HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return strconv.ParseUint(data.(string), 0, 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToIntHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to int.
|
||||||
|
func StringToIntHookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToUintHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to uint.
|
||||||
|
func StringToUintHookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToFloat32HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to float32.
|
||||||
|
func StringToFloat32HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToFloat64HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to float64.
|
||||||
|
func StringToFloat64HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String || t.Kind() != reflect.Float64 {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return strconv.ParseFloat(data.(string), 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToBoolHookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to bool.
|
||||||
|
func StringToBoolHookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String || t.Kind() != reflect.Bool {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return strconv.ParseBool(data.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 interface{}) (interface{}, 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), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToComplex128HookFunc returns a DecodeHookFunc that converts
|
||||||
|
// strings to complex128.
|
||||||
|
func StringToComplex128HookFunc() DecodeHookFunc {
|
||||||
|
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||||
|
if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert it by parsing
|
||||||
|
return strconv.ParseComplex(data.(string), 128)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"cachix": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv_2",
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"devenv",
|
||||||
|
"pre-commit-hooks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712055811,
|
||||||
|
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "cachix",
|
||||||
|
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "cachix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devenv": {
|
||||||
|
"inputs": {
|
||||||
|
"cachix": "cachix",
|
||||||
|
"flake-compat": "flake-compat_2",
|
||||||
|
"nix": "nix_2",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717245169,
|
||||||
|
"narHash": "sha256-+mW3rTBjGU8p1THJN0lX/Dd/8FbnF+3dB+mJuSaxewE=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "c3f9f053c077c6f88a3de5276d9178c62baa3fc3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devenv_2": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"cachix",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"nix": "nix",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"poetry2nix": "poetry2nix",
|
||||||
|
"pre-commit-hooks": [
|
||||||
|
"devenv",
|
||||||
|
"cachix",
|
||||||
|
"pre-commit-hooks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1708704632,
|
||||||
|
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "python-rewrite",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673956053,
|
||||||
|
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat_2": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696426674,
|
||||||
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717285511,
|
||||||
|
"narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689068808,
|
||||||
|
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"pre-commit-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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"cachix",
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-regression": "nixpkgs-regression"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712911606,
|
||||||
|
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"repo": "nix",
|
||||||
|
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"ref": "devenv-2.21",
|
||||||
|
"repo": "nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-github-actions": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"cachix",
|
||||||
|
"devenv",
|
||||||
|
"poetry2nix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1688870561,
|
||||||
|
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-regression": "nixpkgs-regression_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712911606,
|
||||||
|
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"repo": "nix",
|
||||||
|
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "domenkozar",
|
||||||
|
"ref": "devenv-2.21",
|
||||||
|
"repo": "nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1692808169,
|
||||||
|
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717284937,
|
||||||
|
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-regression": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1643052045,
|
||||||
|
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-regression_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1643052045,
|
||||||
|
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710695816,
|
||||||
|
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-23.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1713361204,
|
||||||
|
"narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "rolling",
|
||||||
|
"repo": "devenv-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1717112898,
|
||||||
|
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"poetry2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nix-github-actions": "nix-github-actions",
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"cachix",
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1692876271,
|
||||||
|
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "poetry2nix",
|
||||||
|
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "poetry2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre-commit-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1713775815,
|
||||||
|
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
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 = { config, self', inputs', pkgs, system, ... }: 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
11
vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go
generated
vendored
Normal file
11
vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go
generated
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func Join(errs ...error) error {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
61
vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go
generated
vendored
Normal file
61
vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -9,84 +9,84 @@
|
||||||
//
|
//
|
||||||
// The simplest function to start with is Decode.
|
// The simplest function to start with is Decode.
|
||||||
//
|
//
|
||||||
// Field Tags
|
// # Field Tags
|
||||||
//
|
//
|
||||||
// When decoding to a struct, mapstructure will use the field name by
|
// 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
|
// 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
|
// "Username" then mapstructure will look for a key in the source value
|
||||||
// of "username" (case insensitive).
|
// of "username" (case insensitive).
|
||||||
//
|
//
|
||||||
// type User struct {
|
// type User struct {
|
||||||
// Username string
|
// Username string
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// You can change the behavior of mapstructure by using struct tags.
|
// You can change the behavior of mapstructure by using struct tags.
|
||||||
// The default struct tag that mapstructure looks for is "mapstructure"
|
// The default struct tag that mapstructure looks for is "mapstructure"
|
||||||
// but you can customize it using DecoderConfig.
|
// but you can customize it using DecoderConfig.
|
||||||
//
|
//
|
||||||
// Renaming Fields
|
// # Renaming Fields
|
||||||
//
|
//
|
||||||
// To rename the key that mapstructure looks for, use the "mapstructure"
|
// To rename the key that mapstructure looks for, use the "mapstructure"
|
||||||
// tag and set a value directly. For example, to change the "username" example
|
// tag and set a value directly. For example, to change the "username" example
|
||||||
// above to "user":
|
// above to "user":
|
||||||
//
|
//
|
||||||
// type User struct {
|
// type User struct {
|
||||||
// Username string `mapstructure:"user"`
|
// 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.
|
// Embedded structs are treated as if they're another field with that name.
|
||||||
// By default, the two structs below are equivalent when decoding with
|
// By default, the two structs below are equivalent when decoding with
|
||||||
// mapstructure:
|
// mapstructure:
|
||||||
//
|
//
|
||||||
// type Person struct {
|
// type Person struct {
|
||||||
// Name string
|
// Name string
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// type Friend struct {
|
// type Friend struct {
|
||||||
// Person
|
// Person
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// type Friend struct {
|
// type Friend struct {
|
||||||
// Person Person
|
// Person Person
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// This would require an input that looks like below:
|
// This would require an input that looks like below:
|
||||||
//
|
//
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "person": map[string]interface{}{"name": "alice"},
|
// "person": map[string]interface{}{"name": "alice"},
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// If your "person" value is NOT nested, then you can append ",squash" to
|
// 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
|
// your tag value and mapstructure will treat it as if the embedded struct
|
||||||
// were part of the struct directly. Example:
|
// were part of the struct directly. Example:
|
||||||
//
|
//
|
||||||
// type Friend struct {
|
// type Friend struct {
|
||||||
// Person `mapstructure:",squash"`
|
// Person `mapstructure:",squash"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Now the following input would be accepted:
|
// Now the following input would be accepted:
|
||||||
//
|
//
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "name": "alice",
|
// "name": "alice",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// When decoding from a struct to a map, the squash tag squashes the struct
|
// 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:
|
// 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:
|
// Will be decoded into a map:
|
||||||
//
|
//
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "name": "alice",
|
// "name": "alice",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// DecoderConfig has a field that changes the behavior of mapstructure
|
// DecoderConfig has a field that changes the behavior of mapstructure
|
||||||
// to always squash embedded structs.
|
// to always squash embedded structs.
|
||||||
//
|
//
|
||||||
// Remainder Values
|
// # Remainder Values
|
||||||
//
|
//
|
||||||
// If there are any unmapped keys in the source value, mapstructure by
|
// If there are any unmapped keys in the source value, mapstructure by
|
||||||
// default will silently ignore them. You can error by setting ErrorUnused
|
// default will silently ignore them. You can error by setting ErrorUnused
|
||||||
|
@ -98,20 +98,20 @@
|
||||||
// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
|
// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
|
||||||
// See example below:
|
// See example below:
|
||||||
//
|
//
|
||||||
// type Friend struct {
|
// type Friend struct {
|
||||||
// Name string
|
// Name string
|
||||||
// Other map[string]interface{} `mapstructure:",remain"`
|
// Other map[string]interface{} `mapstructure:",remain"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Given the input below, Other would be populated with the other
|
// Given the input below, Other would be populated with the other
|
||||||
// values that weren't used (everything but "name"):
|
// values that weren't used (everything but "name"):
|
||||||
//
|
//
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "name": "bob",
|
// "name": "bob",
|
||||||
// "address": "123 Maple St.",
|
// "address": "123 Maple St.",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Omit Empty Values
|
// # Omit Empty Values
|
||||||
//
|
//
|
||||||
// When decoding from a struct to any other value, you may use the
|
// 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
|
// ",omitempty" suffix on your tag to omit that value if it equates to
|
||||||
|
@ -122,37 +122,37 @@
|
||||||
// field value is zero and a numeric type, the field is empty, and it won't
|
// 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.
|
||||||
//
|
//
|
||||||
// type Source struct {
|
// type Source struct {
|
||||||
// Age int `mapstructure:",omitempty"`
|
// Age int `mapstructure:",omitempty"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Unexported fields
|
// # Unexported fields
|
||||||
//
|
//
|
||||||
// Since unexported (private) struct fields cannot be set outside the package
|
// Since unexported (private) struct fields cannot be set outside the package
|
||||||
// where they are defined, the decoder will simply skip them.
|
// where they are defined, the decoder will simply skip them.
|
||||||
//
|
//
|
||||||
// For this output type definition:
|
// For this output type definition:
|
||||||
//
|
//
|
||||||
// type Exported struct {
|
// type Exported struct {
|
||||||
// private string // this unexported field will be skipped
|
// private string // this unexported field will be skipped
|
||||||
// Public string
|
// Public string
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Using this map as input:
|
// Using this map as input:
|
||||||
//
|
//
|
||||||
// map[string]interface{}{
|
// map[string]interface{}{
|
||||||
// "private": "I will be ignored",
|
// "private": "I will be ignored",
|
||||||
// "Public": "I made it through!",
|
// "Public": "I made it through!",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The following struct will be decoded:
|
// The following struct will be decoded:
|
||||||
//
|
//
|
||||||
// type Exported struct {
|
// type Exported struct {
|
||||||
// private: "" // field is left with an empty string (zero value)
|
// private: "" // field is left with an empty string (zero value)
|
||||||
// Public: "I made it through!"
|
// Public: "I made it through!"
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Other Configuration
|
// # Other Configuration
|
||||||
//
|
//
|
||||||
// mapstructure is highly configurable. See the DecoderConfig struct
|
// mapstructure is highly configurable. See the DecoderConfig struct
|
||||||
// for other features and options that are supported.
|
// for other features and options that are supported.
|
||||||
|
@ -160,12 +160,13 @@ package mapstructure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-viper/mapstructure/v2/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecodeHookFunc is the callback function that can be used for
|
// DecodeHookFunc is the callback function that can be used for
|
||||||
|
@ -414,7 +415,15 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||||
// Decode decodes the given raw interface to the target pointer specified
|
// Decode decodes the given raw interface to the target pointer specified
|
||||||
// by the configuration.
|
// by the configuration.
|
||||||
func (d *Decoder) Decode(input interface{}) error {
|
func (d *Decoder) Decode(input interface{}) error {
|
||||||
return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes an unknown data type into a specific reflection value.
|
// Decodes an unknown data type into a specific reflection value.
|
||||||
|
@ -458,7 +467,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
|
||||||
var err error
|
var err error
|
||||||
input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
|
input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
return fmt.Errorf("error decoding '%s': %w", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,6 +487,8 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
|
||||||
err = d.decodeUint(name, input, outVal)
|
err = d.decodeUint(name, input, outVal)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
err = d.decodeFloat(name, input, outVal)
|
err = d.decodeFloat(name, input, outVal)
|
||||||
|
case reflect.Complex64:
|
||||||
|
err = d.decodeComplex(name, input, outVal)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
err = d.decodeStruct(name, input, outVal)
|
err = d.decodeStruct(name, input, outVal)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
|
@ -796,6 +807,22 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeComplex(name string, data interface{}, val reflect.Value) error {
|
||||||
|
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||||
|
dataKind := getKind(dataVal)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case dataKind == reflect.Complex64:
|
||||||
|
val.SetComplex(dataVal.Complex())
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||||
|
name, val.Type(), dataVal.Type(), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
|
||||||
valType := val.Type()
|
valType := val.Type()
|
||||||
valKeyType := valType.Key()
|
valKeyType := valType.Key()
|
||||||
|
@ -811,8 +838,14 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
||||||
valMap = reflect.MakeMap(mapType)
|
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
|
// Check input type and based on the input type jump to the proper func
|
||||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
|
||||||
switch dataVal.Kind() {
|
switch dataVal.Kind() {
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return d.decodeMapFromMap(name, dataVal, val, valMap)
|
return d.decodeMapFromMap(name, dataVal, val, valMap)
|
||||||
|
@ -857,7 +890,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
|
||||||
valElemType := valType.Elem()
|
valElemType := valType.Elem()
|
||||||
|
|
||||||
// Accumulate errors
|
// 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 the input data is empty, then we just match what the input data is.
|
||||||
if dataVal.Len() == 0 {
|
if dataVal.Len() == 0 {
|
||||||
|
@ -879,7 +912,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
|
||||||
// First decode the key into the proper type
|
// First decode the key into the proper type
|
||||||
currentKey := reflect.Indirect(reflect.New(valKeyType))
|
currentKey := reflect.Indirect(reflect.New(valKeyType))
|
||||||
if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {
|
if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {
|
||||||
errors = appendErrors(errors, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,7 +920,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
|
||||||
v := dataVal.MapIndex(k).Interface()
|
v := dataVal.MapIndex(k).Interface()
|
||||||
currentVal := reflect.Indirect(reflect.New(valElemType))
|
currentVal := reflect.Indirect(reflect.New(valElemType))
|
||||||
if err := d.decode(fieldName, v, currentVal); err != nil {
|
if err := d.decode(fieldName, v, currentVal); err != nil {
|
||||||
errors = appendErrors(errors, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -897,12 +930,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
|
||||||
// Set the built up map to the value
|
// Set the built up map to the value
|
||||||
val.Set(valMap)
|
val.Set(valMap)
|
||||||
|
|
||||||
// If we had errors, return those
|
return errors.Join(errs...)
|
||||||
if len(errors) > 0 {
|
|
||||||
return &Error{errors}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||||
|
@ -956,6 +984,18 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if strings.Index(tagValue[index+1:], "remain") != -1 {
|
||||||
|
if v.Kind() != reflect.Map {
|
||||||
|
return fmt.Errorf("error remain-tag field with invalid type: '%s'", v.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := v.MapRange()
|
||||||
|
for ptr.Next() {
|
||||||
|
valMap.SetMapIndex(ptr.Key(), ptr.Value())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
|
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
|
||||||
keyName = keyNameTagValue
|
keyName = keyNameTagValue
|
||||||
|
@ -1123,10 +1163,12 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||||
if valSlice.IsNil() || d.config.ZeroFields {
|
if valSlice.IsNil() || d.config.ZeroFields {
|
||||||
// Make a new slice to hold our result, same size as the original data.
|
// Make a new slice to hold our result, same size as the original data.
|
||||||
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
||||||
|
} else if valSlice.Len() > dataVal.Len() {
|
||||||
|
valSlice = valSlice.Slice(0, dataVal.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate any errors
|
// Accumulate any errors
|
||||||
errors := make([]string, 0)
|
var errs []error
|
||||||
|
|
||||||
for i := 0; i < dataVal.Len(); i++ {
|
for i := 0; i < dataVal.Len(); i++ {
|
||||||
currentData := dataVal.Index(i).Interface()
|
currentData := dataVal.Index(i).Interface()
|
||||||
|
@ -1137,19 +1179,14 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||||
|
|
||||||
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
||||||
if err := d.decode(fieldName, currentData, currentField); err != nil {
|
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
|
// Finally, set the value to the slice we built up
|
||||||
val.Set(valSlice)
|
val.Set(valSlice)
|
||||||
|
|
||||||
// If there were errors, we return those
|
return errors.Join(errs...)
|
||||||
if len(errors) > 0 {
|
|
||||||
return &Error{errors}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error {
|
||||||
|
@ -1161,7 +1198,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
|
||||||
|
|
||||||
valArray := val
|
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
|
// Check input type
|
||||||
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
||||||
if d.config.WeaklyTypedInput {
|
if d.config.WeaklyTypedInput {
|
||||||
|
@ -1188,7 +1225,6 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
|
||||||
if dataVal.Len() > arrayType.Len() {
|
if dataVal.Len() > arrayType.Len() {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len())
|
"'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a new array to hold our result, same size as the original data.
|
// Make a new array to hold our result, same size as the original data.
|
||||||
|
@ -1196,7 +1232,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate any errors
|
// Accumulate any errors
|
||||||
errors := make([]string, 0)
|
var errs []error
|
||||||
|
|
||||||
for i := 0; i < dataVal.Len(); i++ {
|
for i := 0; i < dataVal.Len(); i++ {
|
||||||
currentData := dataVal.Index(i).Interface()
|
currentData := dataVal.Index(i).Interface()
|
||||||
|
@ -1204,19 +1240,14 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
|
||||||
|
|
||||||
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
||||||
if err := d.decode(fieldName, currentData, currentField); err != nil {
|
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
|
// Finally, set the value to the array we built up
|
||||||
val.Set(valArray)
|
val.Set(valArray)
|
||||||
|
|
||||||
// If there were errors, we return those
|
return errors.Join(errs...)
|
||||||
if len(errors) > 0 {
|
|
||||||
return &Error{errors}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error {
|
func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error {
|
||||||
|
@ -1278,7 +1309,8 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
}
|
}
|
||||||
|
|
||||||
targetValKeysUnused := make(map[interface{}]struct{})
|
targetValKeysUnused := make(map[interface{}]struct{})
|
||||||
errors := make([]string, 0)
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
// This slice will keep track of all the structs we'll be decoding.
|
// 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
|
// There can be more than one struct if there are embedded structs
|
||||||
|
@ -1332,8 +1364,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
|
|
||||||
if squash {
|
if squash {
|
||||||
if fieldVal.Kind() != reflect.Struct {
|
if fieldVal.Kind() != reflect.Struct {
|
||||||
errors = appendErrors(errors,
|
errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
|
||||||
fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
|
|
||||||
} else {
|
} else {
|
||||||
structs = append(structs, fieldVal)
|
structs = append(structs, fieldVal)
|
||||||
}
|
}
|
||||||
|
@ -1356,6 +1387,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
fieldName := field.Name
|
fieldName := field.Name
|
||||||
|
|
||||||
tagValue := field.Tag.Get(d.config.TagName)
|
tagValue := field.Tag.Get(d.config.TagName)
|
||||||
|
if tagValue == "" && d.config.IgnoreUntaggedFields {
|
||||||
|
continue
|
||||||
|
}
|
||||||
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
||||||
if tagValue != "" {
|
if tagValue != "" {
|
||||||
fieldName = tagValue
|
fieldName = tagValue
|
||||||
|
@ -1409,7 +1443,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
|
if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
|
||||||
errors = appendErrors(errors, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1424,7 +1458,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
|
|
||||||
// Decode it as-if we were just decoding this map onto our map.
|
// Decode it as-if we were just decoding this map onto our map.
|
||||||
if err := d.decodeMap(name, remain, remainField.val); err != nil {
|
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
|
// Set the map to nil so we have none so that the next check will
|
||||||
|
@ -1440,7 +1474,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
|
err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
|
||||||
errors = appendErrors(errors, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
|
if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
|
||||||
|
@ -1451,11 +1485,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
|
err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
|
||||||
errors = appendErrors(errors, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if err := errors.Join(errs...); err != nil {
|
||||||
return &Error{errors}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the unused keys to the list of unused keys if we're tracking metadata
|
// Add the unused keys to the list of unused keys if we're tracking metadata
|
||||||
|
@ -1509,6 +1543,8 @@ func getKind(val reflect.Value) reflect.Kind {
|
||||||
return reflect.Uint
|
return reflect.Uint
|
||||||
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
||||||
return reflect.Float32
|
return reflect.Float32
|
||||||
|
case kind >= reflect.Complex64 && kind <= reflect.Complex128:
|
||||||
|
return reflect.Complex64
|
||||||
default:
|
default:
|
||||||
return kind
|
return kind
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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.
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -129,6 +129,10 @@ github.com/go-logr/logr/funcr
|
||||||
# github.com/go-logr/stdr v1.2.2
|
# github.com/go-logr/stdr v1.2.2
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/go-logr/stdr
|
github.com/go-logr/stdr
|
||||||
|
# github.com/go-viper/mapstructure/v2 v2.0.0
|
||||||
|
## explicit; go 1.18
|
||||||
|
github.com/go-viper/mapstructure/v2
|
||||||
|
github.com/go-viper/mapstructure/v2/internal/errors
|
||||||
# github.com/gogo/protobuf v1.3.2
|
# github.com/gogo/protobuf v1.3.2
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/gogo/protobuf/gogoproto
|
github.com/gogo/protobuf/gogoproto
|
||||||
|
@ -184,9 +188,6 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||||
# github.com/miekg/pkcs11 v1.1.1
|
# github.com/miekg/pkcs11 v1.1.1
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/miekg/pkcs11
|
github.com/miekg/pkcs11
|
||||||
# github.com/mitchellh/mapstructure v1.5.0
|
|
||||||
## explicit; go 1.14
|
|
||||||
github.com/mitchellh/mapstructure
|
|
||||||
# github.com/moby/docker-image-spec v1.3.1
|
# github.com/moby/docker-image-spec v1.3.1
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/moby/docker-image-spec/specs-go/v1
|
github.com/moby/docker-image-spec/specs-go/v1
|
||||||
|
|
Loading…
Reference in New Issue