Path: blob/main/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go
3434 views
// Package mapstructure exposes functionality to convert one arbitrary1// Go type into another, typically to convert a map[string]any2// into a native Go structure.3//4// The Go structure can be arbitrarily complex, containing slices,5// other structs, etc. and the decoder will properly decode nested6// maps and so on into the proper structures in the native Go struct.7// See the examples to see what the decoder is capable of.8//9// The simplest function to start with is Decode.10//11// # Field Tags12//13// When decoding to a struct, mapstructure will use the field name by14// default to perform the mapping. For example, if a struct has a field15// "Username" then mapstructure will look for a key in the source value16// of "username" (case insensitive).17//18// type User struct {19// Username string20// }21//22// You can change the behavior of mapstructure by using struct tags.23// The default struct tag that mapstructure looks for is "mapstructure"24// but you can customize it using DecoderConfig.25//26// # Renaming Fields27//28// To rename the key that mapstructure looks for, use the "mapstructure"29// tag and set a value directly. For example, to change the "username" example30// above to "user":31//32// type User struct {33// Username string `mapstructure:"user"`34// }35//36// # Embedded Structs and Squashing37//38// Embedded structs are treated as if they're another field with that name.39// By default, the two structs below are equivalent when decoding with40// mapstructure:41//42// type Person struct {43// Name string44// }45//46// type Friend struct {47// Person48// }49//50// type Friend struct {51// Person Person52// }53//54// This would require an input that looks like below:55//56// map[string]any{57// "person": map[string]any{"name": "alice"},58// }59//60// If your "person" value is NOT nested, then you can append ",squash" to61// your tag value and mapstructure will treat it as if the embedded struct62// were part of the struct directly. Example:63//64// type Friend struct {65// Person `mapstructure:",squash"`66// }67//68// Now the following input would be accepted:69//70// map[string]any{71// "name": "alice",72// }73//74// When decoding from a struct to a map, the squash tag squashes the struct75// fields into a single map. Using the example structs from above:76//77// Friend{Person: Person{Name: "alice"}}78//79// Will be decoded into a map:80//81// map[string]any{82// "name": "alice",83// }84//85// DecoderConfig has a field that changes the behavior of mapstructure86// to always squash embedded structs.87//88// # Remainder Values89//90// If there are any unmapped keys in the source value, mapstructure by91// default will silently ignore them. You can error by setting ErrorUnused92// in DecoderConfig. If you're using Metadata you can also maintain a slice93// of the unused keys.94//95// You can also use the ",remain" suffix on your tag to collect all unused96// values in a map. The field with this tag MUST be a map type and should97// probably be a "map[string]any" or "map[any]any".98// See example below:99//100// type Friend struct {101// Name string102// Other map[string]any `mapstructure:",remain"`103// }104//105// Given the input below, Other would be populated with the other106// values that weren't used (everything but "name"):107//108// map[string]any{109// "name": "bob",110// "address": "123 Maple St.",111// }112//113// # Omit Empty Values114//115// When decoding from a struct to any other value, you may use the116// ",omitempty" suffix on your tag to omit that value if it equates to117// the zero value, or a zero-length element. The zero value of all types is118// specified in the Go specification.119//120// For example, the zero type of a numeric type is zero ("0"). If the struct121// field value is zero and a numeric type, the field is empty, and it won't122// be encoded into the destination type. And likewise for the URLs field, if the123// slice is nil or empty, it won't be encoded into the destination type.124//125// type Source struct {126// Age int `mapstructure:",omitempty"`127// URLs []string `mapstructure:",omitempty"`128// }129//130// # Omit Zero Values131//132// When decoding from a struct to any other value, you may use the133// ",omitzero" suffix on your tag to omit that value if it equates to the zero134// value. The zero value of all types is specified in the Go specification.135//136// For example, the zero type of a numeric type is zero ("0"). If the struct137// field value is zero and a numeric type, the field is empty, and it won't138// be encoded into the destination type. And likewise for the URLs field, if the139// slice is nil, it won't be encoded into the destination type.140//141// Note that if the field is a slice, and it is empty but not nil, it will142// still be encoded into the destination type.143//144// type Source struct {145// Age int `mapstructure:",omitzero"`146// URLs []string `mapstructure:",omitzero"`147// }148//149// # Unexported fields150//151// Since unexported (private) struct fields cannot be set outside the package152// where they are defined, the decoder will simply skip them.153//154// For this output type definition:155//156// type Exported struct {157// private string // this unexported field will be skipped158// Public string159// }160//161// Using this map as input:162//163// map[string]any{164// "private": "I will be ignored",165// "Public": "I made it through!",166// }167//168// The following struct will be decoded:169//170// type Exported struct {171// private: "" // field is left with an empty string (zero value)172// Public: "I made it through!"173// }174//175// # Custom Decoding with Unmarshaler176//177// Types can implement the Unmarshaler interface to control their own decoding. The interface178// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an179// alternative or companion to a DecodeHook.180//181// type TrimmedString string182//183// func (t *TrimmedString) UnmarshalMapstructure(input any) error {184// str, ok := input.(string)185// if !ok {186// return fmt.Errorf("expected string, got %T", input)187// }188// *t = TrimmedString(strings.TrimSpace(str))189// return nil190// }191//192// See the Unmarshaler interface documentation for more details.193//194// # Other Configuration195//196// mapstructure is highly configurable. See the DecoderConfig struct197// for other features and options that are supported.198package mapstructure199200import (201"encoding/json"202"fmt"203"reflect"204"sort"205"strconv"206"strings"207208"github.com/go-viper/mapstructure/v2/internal/errors"209)210211// DecodeHookFunc is the callback function that can be used for212// data transformations. See "DecodeHook" in the DecoderConfig213// struct.214//215// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or216// DecodeHookFuncValue.217// Values are a superset of Types (Values can return types), and Types are a218// superset of Kinds (Types can return Kinds) and are generally a richer thing219// to use, but Kinds are simpler if you only need those.220//221// The reason DecodeHookFunc is multi-typed is for backwards compatibility:222// we started with Kinds and then realized Types were the better solution,223// but have a promise to not break backwards compat so we now support224// both.225type DecodeHookFunc any226227// DecodeHookFuncType is a DecodeHookFunc which has complete information about228// the source and target types.229type DecodeHookFuncType func(reflect.Type, reflect.Type, any) (any, error)230231// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the232// source and target types.233type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)234235// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target236// values.237type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)238239// Unmarshaler is the interface implemented by types that can unmarshal240// themselves. UnmarshalMapstructure receives the input data (potentially241// transformed by DecodeHook) and should populate the receiver with the242// decoded values.243//244// The Unmarshaler interface takes precedence over the default decoding245// logic for any type (structs, slices, maps, primitives, etc.).246type Unmarshaler interface {247UnmarshalMapstructure(any) error248}249250// DecoderConfig is the configuration that is used to create a new decoder251// and allows customization of various aspects of decoding.252type DecoderConfig struct {253// DecodeHook, if set, will be called before any decoding and any254// type conversion (if WeaklyTypedInput is on). This lets you modify255// the values before they're set down onto the resulting struct. The256// DecodeHook is called for every map and value in the input. This means257// that if a struct has embedded fields with squash tags the decode hook258// is called only once with all of the input data, not once for each259// embedded struct.260//261// If an error is returned, the entire decode will fail with that error.262DecodeHook DecodeHookFunc263264// If ErrorUnused is true, then it is an error for there to exist265// keys in the original map that were unused in the decoding process266// (extra keys).267ErrorUnused bool268269// If ErrorUnset is true, then it is an error for there to exist270// fields in the result that were not set in the decoding process271// (extra fields). This only applies to decoding to a struct. This272// will affect all nested structs as well.273ErrorUnset bool274275// AllowUnsetPointer, if set to true, will prevent fields with pointer types276// from being reported as unset, even if ErrorUnset is true and the field was277// not present in the input data. This allows pointer fields to be optional278// without triggering an error when they are missing.279AllowUnsetPointer bool280281// ZeroFields, if set to true, will zero fields before writing them.282// For example, a map will be emptied before decoded values are put in283// it. If this is false, a map will be merged.284ZeroFields bool285286// If WeaklyTypedInput is true, the decoder will make the following287// "weak" conversions:288//289// - bools to string (true = "1", false = "0")290// - numbers to string (base 10)291// - bools to int/uint (true = 1, false = 0)292// - strings to int/uint (base implied by prefix)293// - int to bool (true if value != 0)294// - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,295// FALSE, false, False. Anything else is an error)296// - empty array = empty map and vice versa297// - negative numbers to overflowed uint values (base 10)298// - slice of maps to a merged map299// - single values are converted to slices if required. Each300// element is weakly decoded. For example: "4" can become []int{4}301// if the target type is an int slice.302//303WeaklyTypedInput bool304305// Squash will squash embedded structs. A squash tag may also be306// added to an individual struct field using a tag. For example:307//308// type Parent struct {309// Child `mapstructure:",squash"`310// }311Squash bool312313// Deep will map structures in slices instead of copying them314//315// type Parent struct {316// Children []Child `mapstructure:",deep"`317// }318Deep bool319320// Metadata is the struct that will contain extra metadata about321// the decoding. If this is nil, then no metadata will be tracked.322Metadata *Metadata323324// Result is a pointer to the struct that will contain the decoded325// value.326Result any327328// The tag name that mapstructure reads for field names. This329// defaults to "mapstructure". Multiple tag names can be specified330// as a comma-separated list (e.g., "yaml,json"), and the first331// matching non-empty tag will be used.332TagName string333334// RootName specifies the name to use for the root element in error messages. For example:335// '<rootName>' has unset fields: <fieldName>336RootName string337338// The option of the value in the tag that indicates a field should339// be squashed. This defaults to "squash".340SquashTagOption string341342// IgnoreUntaggedFields ignores all struct fields without explicit343// TagName, comparable to `mapstructure:"-"` as default behaviour.344IgnoreUntaggedFields bool345346// MatchName is the function used to match the map key to the struct347// field name or tag. Defaults to `strings.EqualFold`. This can be used348// to implement case-sensitive tag values, support snake casing, etc.349//350// MatchName is used as a fallback comparison when the direct key lookup fails.351// See also MapFieldName for transforming field names before lookup.352MatchName func(mapKey, fieldName string) bool353354// DecodeNil, if set to true, will cause the DecodeHook (if present) to run355// even if the input is nil. This can be used to provide default values.356DecodeNil bool357358// MapFieldName is the function used to convert the struct field name to the map's key name.359//360// This is useful for automatically converting between naming conventions without361// explicitly tagging each field. For example, to convert Go's PascalCase field names362// to snake_case map keys:363//364// MapFieldName: func(s string) string {365// return strcase.ToSnake(s)366// }367//368// When decoding from a map to a struct, the transformed field name is used for369// the initial lookup. If not found, MatchName is used as a fallback comparison.370// Explicit struct tags always take precedence over MapFieldName.371MapFieldName func(string) string372373// DisableUnmarshaler, if set to true, disables the use of the Unmarshaler374// interface. Types implementing Unmarshaler will be decoded using the375// standard struct decoding logic instead.376DisableUnmarshaler bool377}378379// A Decoder takes a raw interface value and turns it into structured380// data, keeping track of rich error information along the way in case381// anything goes wrong. Unlike the basic top-level Decode method, you can382// more finely control how the Decoder behaves using the DecoderConfig383// structure. The top-level Decode method is just a convenience that sets384// up the most basic Decoder.385type Decoder struct {386config *DecoderConfig387cachedDecodeHook func(from reflect.Value, to reflect.Value) (any, error)388}389390// Metadata contains information about decoding a structure that391// is tedious or difficult to get otherwise.392type Metadata struct {393// Keys are the keys of the structure which were successfully decoded394Keys []string395396// Unused is a slice of keys that were found in the raw value but397// weren't decoded since there was no matching field in the result interface398Unused []string399400// Unset is a slice of field names that were found in the result interface401// but weren't set in the decoding process since there was no matching value402// in the input403Unset []string404}405406// Decode takes an input structure and uses reflection to translate it to407// the output structure. output must be a pointer to a map or struct.408func Decode(input any, output any) error {409config := &DecoderConfig{410Metadata: nil,411Result: output,412}413414decoder, err := NewDecoder(config)415if err != nil {416return err417}418419return decoder.Decode(input)420}421422// WeakDecode is the same as Decode but is shorthand to enable423// WeaklyTypedInput. See DecoderConfig for more info.424func WeakDecode(input, output any) error {425config := &DecoderConfig{426Metadata: nil,427Result: output,428WeaklyTypedInput: true,429}430431decoder, err := NewDecoder(config)432if err != nil {433return err434}435436return decoder.Decode(input)437}438439// DecodeMetadata is the same as Decode, but is shorthand to440// enable metadata collection. See DecoderConfig for more info.441func DecodeMetadata(input any, output any, metadata *Metadata) error {442config := &DecoderConfig{443Metadata: metadata,444Result: output,445}446447decoder, err := NewDecoder(config)448if err != nil {449return err450}451452return decoder.Decode(input)453}454455// WeakDecodeMetadata is the same as Decode, but is shorthand to456// enable both WeaklyTypedInput and metadata collection. See457// DecoderConfig for more info.458func WeakDecodeMetadata(input any, output any, metadata *Metadata) error {459config := &DecoderConfig{460Metadata: metadata,461Result: output,462WeaklyTypedInput: true,463}464465decoder, err := NewDecoder(config)466if err != nil {467return err468}469470return decoder.Decode(input)471}472473// NewDecoder returns a new decoder for the given configuration. Once474// a decoder has been returned, the same configuration must not be used475// again.476func NewDecoder(config *DecoderConfig) (*Decoder, error) {477val := reflect.ValueOf(config.Result)478if val.Kind() != reflect.Ptr {479return nil, errors.New("result must be a pointer")480}481482val = val.Elem()483if !val.CanAddr() {484return nil, errors.New("result must be addressable (a pointer)")485}486487if config.Metadata != nil {488if config.Metadata.Keys == nil {489config.Metadata.Keys = make([]string, 0)490}491492if config.Metadata.Unused == nil {493config.Metadata.Unused = make([]string, 0)494}495496if config.Metadata.Unset == nil {497config.Metadata.Unset = make([]string, 0)498}499}500501if config.TagName == "" {502config.TagName = "mapstructure"503}504505if config.SquashTagOption == "" {506config.SquashTagOption = "squash"507}508509if config.MatchName == nil {510config.MatchName = strings.EqualFold511}512513if config.MapFieldName == nil {514config.MapFieldName = func(s string) string {515return s516}517}518519result := &Decoder{520config: config,521}522if config.DecodeHook != nil {523result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook)524}525526return result, nil527}528529// Decode decodes the given raw interface to the target pointer specified530// by the configuration.531func (d *Decoder) Decode(input any) error {532err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem())533534// Retain some of the original behavior when multiple errors ocurr535var joinedErr interface{ Unwrap() []error }536if errors.As(err, &joinedErr) {537return fmt.Errorf("decoding failed due to the following error(s):\n\n%w", err)538}539540return err541}542543// isNil returns true if the input is nil or a typed nil pointer.544func isNil(input any) bool {545if input == nil {546return true547}548val := reflect.ValueOf(input)549return val.Kind() == reflect.Ptr && val.IsNil()550}551552// Decodes an unknown data type into a specific reflection value.553func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {554var (555inputVal = reflect.ValueOf(input)556outputKind = getKind(outVal)557decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil558)559if isNil(input) {560// Typed nils won't match the "input == nil" below, so reset input.561input = nil562}563if input == nil {564// If the data is nil, then we don't set anything, unless ZeroFields is set565// to true.566if d.config.ZeroFields {567outVal.Set(reflect.Zero(outVal.Type()))568569if d.config.Metadata != nil && name != "" {570d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)571}572}573if !decodeNil {574return nil575}576}577if !inputVal.IsValid() {578if !decodeNil {579// If the input value is invalid, then we just set the value580// to be the zero value.581outVal.Set(reflect.Zero(outVal.Type()))582if d.config.Metadata != nil && name != "" {583d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)584}585return nil586}587// Hooks need a valid inputVal, so reset it to zero value of outVal type.588switch outputKind {589case reflect.Struct, reflect.Map:590var mapVal map[string]any591inputVal = reflect.ValueOf(mapVal) // create nil map pointer592case reflect.Slice, reflect.Array:593var sliceVal []any594inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer595default:596inputVal = reflect.Zero(outVal.Type())597}598}599600if d.cachedDecodeHook != nil {601// We have a DecodeHook, so let's pre-process the input.602var err error603input, err = d.cachedDecodeHook(inputVal, outVal)604if err != nil {605return newDecodeError(name, err)606}607}608if isNil(input) {609return nil610}611612var err error613addMetaKey := true614615// Check if the target implements Unmarshaler and use it if not disabled616unmarshaled := false617if !d.config.DisableUnmarshaler {618if unmarshaler, ok := getUnmarshaler(outVal); ok {619if err = unmarshaler.UnmarshalMapstructure(input); err != nil {620err = newDecodeError(name, err)621}622unmarshaled = true623}624}625626if !unmarshaled {627switch outputKind {628case reflect.Bool:629err = d.decodeBool(name, input, outVal)630case reflect.Interface:631err = d.decodeBasic(name, input, outVal)632case reflect.String:633err = d.decodeString(name, input, outVal)634case reflect.Int:635err = d.decodeInt(name, input, outVal)636case reflect.Uint:637err = d.decodeUint(name, input, outVal)638case reflect.Float32:639err = d.decodeFloat(name, input, outVal)640case reflect.Complex64:641err = d.decodeComplex(name, input, outVal)642case reflect.Struct:643err = d.decodeStruct(name, input, outVal)644case reflect.Map:645err = d.decodeMap(name, input, outVal)646case reflect.Ptr:647addMetaKey, err = d.decodePtr(name, input, outVal)648case reflect.Slice:649err = d.decodeSlice(name, input, outVal)650case reflect.Array:651err = d.decodeArray(name, input, outVal)652case reflect.Func:653err = d.decodeFunc(name, input, outVal)654default:655// If we reached this point then we weren't able to decode it656return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))657}658}659660// If we reached here, then we successfully decoded SOMETHING, so661// mark the key as used if we're tracking metainput.662if addMetaKey && d.config.Metadata != nil && name != "" {663d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)664}665666return err667}668669// This decodes a basic type (bool, int, string, etc.) and sets the670// value to "data" of that type.671func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error {672if val.IsValid() && val.Elem().IsValid() {673elem := val.Elem()674675// If we can't address this element, then its not writable. Instead,676// we make a copy of the value (which is a pointer and therefore677// writable), decode into that, and replace the whole value.678copied := false679if !elem.CanAddr() {680copied = true681682// Make *T683copy := reflect.New(elem.Type())684685// *T = elem686copy.Elem().Set(elem)687688// Set elem so we decode into it689elem = copy690}691692// Decode. If we have an error then return. We also return right693// away if we're not a copy because that means we decoded directly.694if err := d.decode(name, data, elem); err != nil || !copied {695return err696}697698// If we're a copy, we need to set te final result699val.Set(elem.Elem())700return nil701}702703dataVal := reflect.ValueOf(data)704705// If the input data is a pointer, and the assigned type is the dereference706// of that exact pointer, then indirect it so that we can assign it.707// Example: *string to string708if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {709dataVal = reflect.Indirect(dataVal)710}711712if !dataVal.IsValid() {713dataVal = reflect.Zero(val.Type())714}715716dataValType := dataVal.Type()717if !dataValType.AssignableTo(val.Type()) {718return newDecodeError(name, &UnconvertibleTypeError{719Expected: val,720Value: data,721})722}723724val.Set(dataVal)725return nil726}727728func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {729dataVal := reflect.Indirect(reflect.ValueOf(data))730dataKind := getKind(dataVal)731732converted := true733switch {734case dataKind == reflect.String:735val.SetString(dataVal.String())736case dataKind == reflect.Bool && d.config.WeaklyTypedInput:737if dataVal.Bool() {738val.SetString("1")739} else {740val.SetString("0")741}742case dataKind == reflect.Int && d.config.WeaklyTypedInput:743val.SetString(strconv.FormatInt(dataVal.Int(), 10))744case dataKind == reflect.Uint && d.config.WeaklyTypedInput:745val.SetString(strconv.FormatUint(dataVal.Uint(), 10))746case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:747val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))748case dataKind == reflect.Slice && d.config.WeaklyTypedInput,749dataKind == reflect.Array && d.config.WeaklyTypedInput:750dataType := dataVal.Type()751elemKind := dataType.Elem().Kind()752switch elemKind {753case reflect.Uint8:754var uints []uint8755if dataKind == reflect.Array {756uints = make([]uint8, dataVal.Len())757for i := range uints {758uints[i] = dataVal.Index(i).Interface().(uint8)759}760} else {761uints = dataVal.Interface().([]uint8)762}763val.SetString(string(uints))764default:765converted = false766}767default:768converted = false769}770771if !converted {772return newDecodeError(name, &UnconvertibleTypeError{773Expected: val,774Value: data,775})776}777778return nil779}780781func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error {782dataVal := reflect.Indirect(reflect.ValueOf(data))783dataKind := getKind(dataVal)784dataType := dataVal.Type()785786switch {787case dataKind == reflect.Int:788val.SetInt(dataVal.Int())789case dataKind == reflect.Uint:790val.SetInt(int64(dataVal.Uint()))791case dataKind == reflect.Float32:792val.SetInt(int64(dataVal.Float()))793case dataKind == reflect.Bool && d.config.WeaklyTypedInput:794if dataVal.Bool() {795val.SetInt(1)796} else {797val.SetInt(0)798}799case dataKind == reflect.String && d.config.WeaklyTypedInput:800str := dataVal.String()801if str == "" {802str = "0"803}804805i, err := strconv.ParseInt(str, 0, val.Type().Bits())806if err == nil {807val.SetInt(i)808} else {809return newDecodeError(name, &ParseError{810Expected: val,811Value: data,812Err: wrapStrconvNumError(err),813})814}815case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":816jn := data.(json.Number)817i, err := jn.Int64()818if err != nil {819return newDecodeError(name, &ParseError{820Expected: val,821Value: data,822Err: err,823})824}825val.SetInt(i)826default:827return newDecodeError(name, &UnconvertibleTypeError{828Expected: val,829Value: data,830})831}832833return nil834}835836func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error {837dataVal := reflect.Indirect(reflect.ValueOf(data))838dataKind := getKind(dataVal)839dataType := dataVal.Type()840841switch {842case dataKind == reflect.Int:843i := dataVal.Int()844if i < 0 && !d.config.WeaklyTypedInput {845return newDecodeError(name, &ParseError{846Expected: val,847Value: data,848Err: fmt.Errorf("%d overflows uint", i),849})850}851val.SetUint(uint64(i))852case dataKind == reflect.Uint:853val.SetUint(dataVal.Uint())854case dataKind == reflect.Float32:855f := dataVal.Float()856if f < 0 && !d.config.WeaklyTypedInput {857return newDecodeError(name, &ParseError{858Expected: val,859Value: data,860Err: fmt.Errorf("%f overflows uint", f),861})862}863val.SetUint(uint64(f))864case dataKind == reflect.Bool && d.config.WeaklyTypedInput:865if dataVal.Bool() {866val.SetUint(1)867} else {868val.SetUint(0)869}870case dataKind == reflect.String && d.config.WeaklyTypedInput:871str := dataVal.String()872if str == "" {873str = "0"874}875876i, err := strconv.ParseUint(str, 0, val.Type().Bits())877if err == nil {878val.SetUint(i)879} else {880return newDecodeError(name, &ParseError{881Expected: val,882Value: data,883Err: wrapStrconvNumError(err),884})885}886case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":887jn := data.(json.Number)888i, err := strconv.ParseUint(string(jn), 0, 64)889if err != nil {890return newDecodeError(name, &ParseError{891Expected: val,892Value: data,893Err: wrapStrconvNumError(err),894})895}896val.SetUint(i)897default:898return newDecodeError(name, &UnconvertibleTypeError{899Expected: val,900Value: data,901})902}903904return nil905}906907func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error {908dataVal := reflect.Indirect(reflect.ValueOf(data))909dataKind := getKind(dataVal)910911switch {912case dataKind == reflect.Bool:913val.SetBool(dataVal.Bool())914case dataKind == reflect.Int && d.config.WeaklyTypedInput:915val.SetBool(dataVal.Int() != 0)916case dataKind == reflect.Uint && d.config.WeaklyTypedInput:917val.SetBool(dataVal.Uint() != 0)918case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:919val.SetBool(dataVal.Float() != 0)920case dataKind == reflect.String && d.config.WeaklyTypedInput:921b, err := strconv.ParseBool(dataVal.String())922if err == nil {923val.SetBool(b)924} else if dataVal.String() == "" {925val.SetBool(false)926} else {927return newDecodeError(name, &ParseError{928Expected: val,929Value: data,930Err: wrapStrconvNumError(err),931})932}933default:934return newDecodeError(name, &UnconvertibleTypeError{935Expected: val,936Value: data,937})938}939940return nil941}942943func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error {944dataVal := reflect.Indirect(reflect.ValueOf(data))945dataKind := getKind(dataVal)946dataType := dataVal.Type()947948switch {949case dataKind == reflect.Int:950val.SetFloat(float64(dataVal.Int()))951case dataKind == reflect.Uint:952val.SetFloat(float64(dataVal.Uint()))953case dataKind == reflect.Float32:954val.SetFloat(dataVal.Float())955case dataKind == reflect.Bool && d.config.WeaklyTypedInput:956if dataVal.Bool() {957val.SetFloat(1)958} else {959val.SetFloat(0)960}961case dataKind == reflect.String && d.config.WeaklyTypedInput:962str := dataVal.String()963if str == "" {964str = "0"965}966967f, err := strconv.ParseFloat(str, val.Type().Bits())968if err == nil {969val.SetFloat(f)970} else {971return newDecodeError(name, &ParseError{972Expected: val,973Value: data,974Err: wrapStrconvNumError(err),975})976}977case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":978jn := data.(json.Number)979i, err := jn.Float64()980if err != nil {981return newDecodeError(name, &ParseError{982Expected: val,983Value: data,984Err: err,985})986}987val.SetFloat(i)988default:989return newDecodeError(name, &UnconvertibleTypeError{990Expected: val,991Value: data,992})993}994995return nil996}997998func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error {999dataVal := reflect.Indirect(reflect.ValueOf(data))1000dataKind := getKind(dataVal)10011002switch {1003case dataKind == reflect.Complex64:1004val.SetComplex(dataVal.Complex())1005default:1006return newDecodeError(name, &UnconvertibleTypeError{1007Expected: val,1008Value: data,1009})1010}10111012return nil1013}10141015func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error {1016valType := val.Type()1017valKeyType := valType.Key()1018valElemType := valType.Elem()10191020// By default we overwrite keys in the current map1021valMap := val10221023// If the map is nil or we're purposely zeroing fields, make a new map1024if valMap.IsNil() || d.config.ZeroFields {1025// Make a new map to hold our result1026mapType := reflect.MapOf(valKeyType, valElemType)1027valMap = reflect.MakeMap(mapType)1028}10291030dataVal := reflect.ValueOf(data)10311032// Resolve any levels of indirection1033for dataVal.Kind() == reflect.Pointer {1034dataVal = reflect.Indirect(dataVal)1035}10361037// Check input type and based on the input type jump to the proper func1038switch dataVal.Kind() {1039case reflect.Map:1040return d.decodeMapFromMap(name, dataVal, val, valMap)10411042case reflect.Struct:1043return d.decodeMapFromStruct(name, dataVal, val, valMap)10441045case reflect.Array, reflect.Slice:1046if d.config.WeaklyTypedInput {1047return d.decodeMapFromSlice(name, dataVal, val, valMap)1048}10491050fallthrough10511052default:1053return newDecodeError(name, &UnconvertibleTypeError{1054Expected: val,1055Value: data,1056})1057}1058}10591060func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {1061// Special case for BC reasons (covered by tests)1062if dataVal.Len() == 0 {1063val.Set(valMap)1064return nil1065}10661067for i := 0; i < dataVal.Len(); i++ {1068err := d.decode(1069name+"["+strconv.Itoa(i)+"]",1070dataVal.Index(i).Interface(), val)1071if err != nil {1072return err1073}1074}10751076return nil1077}10781079func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {1080valType := val.Type()1081valKeyType := valType.Key()1082valElemType := valType.Elem()10831084// Accumulate errors1085var errs []error10861087// If the input data is empty, then we just match what the input data is.1088if dataVal.Len() == 0 {1089if dataVal.IsNil() {1090if !val.IsNil() {1091val.Set(dataVal)1092}1093} else {1094// Set to empty allocated value1095val.Set(valMap)1096}10971098return nil1099}11001101for _, k := range dataVal.MapKeys() {1102fieldName := name + "[" + k.String() + "]"11031104// First decode the key into the proper type1105currentKey := reflect.Indirect(reflect.New(valKeyType))1106if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {1107errs = append(errs, err)1108continue1109}11101111// Next decode the data into the proper type1112v := dataVal.MapIndex(k).Interface()1113currentVal := reflect.Indirect(reflect.New(valElemType))1114if err := d.decode(fieldName, v, currentVal); err != nil {1115errs = append(errs, err)1116continue1117}11181119valMap.SetMapIndex(currentKey, currentVal)1120}11211122// Set the built up map to the value1123val.Set(valMap)11241125return errors.Join(errs...)1126}11271128func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {1129typ := dataVal.Type()1130for i := 0; i < typ.NumField(); i++ {1131// Get the StructField first since this is a cheap operation. If the1132// field is unexported, then ignore it.1133f := typ.Field(i)1134if f.PkgPath != "" {1135continue1136}11371138// Next get the actual value of this field and verify it is assignable1139// to the map value.1140v := dataVal.Field(i)1141if !v.Type().AssignableTo(valMap.Type().Elem()) {1142return newDecodeError(1143name+"."+f.Name,1144fmt.Errorf("cannot assign type %q to map value field of type %q", v.Type(), valMap.Type().Elem()),1145)1146}11471148tagValue, _ := getTagValue(f, d.config.TagName)1149keyName := d.config.MapFieldName(f.Name)11501151if tagValue == "" && d.config.IgnoreUntaggedFields {1152continue1153}11541155// If Squash is set in the config, we squash the field down.1156squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous11571158// If Deep is set in the config, set as default value.1159deep := d.config.Deep11601161v = dereferencePtrToStructIfNeeded(v, d.config.TagName)11621163// Determine the name of the key in the map1164if index := strings.Index(tagValue, ","); index != -1 {1165if tagValue[:index] == "-" {1166continue1167}1168// If "omitempty" is specified in the tag, it ignores empty values.1169if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) {1170continue1171}11721173// If "omitzero" is specified in the tag, it ignores zero values.1174if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() {1175continue1176}11771178// If "squash" is specified in the tag, we squash the field down.1179squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)1180if squash {1181// When squashing, the embedded type can be a pointer to a struct.1182if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {1183v = v.Elem()1184}11851186// The final type must be a struct1187if v.Kind() != reflect.Struct {1188return newDecodeError(1189name+"."+f.Name,1190fmt.Errorf("cannot squash non-struct type %q", v.Type()),1191)1192}1193} else {1194if strings.Contains(tagValue[index+1:], "remain") {1195if v.Kind() != reflect.Map {1196return newDecodeError(1197name+"."+f.Name,1198fmt.Errorf("error remain-tag field with invalid type: %q", v.Type()),1199)1200}12011202ptr := v.MapRange()1203for ptr.Next() {1204valMap.SetMapIndex(ptr.Key(), ptr.Value())1205}1206continue1207}1208}12091210deep = deep || strings.Contains(tagValue[index+1:], "deep")12111212if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {1213keyName = keyNameTagValue1214}1215} else if len(tagValue) > 0 {1216if tagValue == "-" {1217continue1218}1219keyName = tagValue1220}12211222switch v.Kind() {1223// this is an embedded struct, so handle it differently1224case reflect.Struct:1225x := reflect.New(v.Type())1226x.Elem().Set(v)12271228vType := valMap.Type()1229vKeyType := vType.Key()1230vElemType := vType.Elem()1231mType := reflect.MapOf(vKeyType, vElemType)1232vMap := reflect.MakeMap(mType)12331234// Creating a pointer to a map so that other methods can completely1235// overwrite the map if need be (looking at you decodeMapFromMap). The1236// indirection allows the underlying map to be settable (CanSet() == true)1237// where as reflect.MakeMap returns an unsettable map.1238addrVal := reflect.New(vMap.Type())1239reflect.Indirect(addrVal).Set(vMap)12401241err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))1242if err != nil {1243return err1244}12451246// the underlying map may have been completely overwritten so pull1247// it indirectly out of the enclosing value.1248vMap = reflect.Indirect(addrVal)12491250if squash {1251for _, k := range vMap.MapKeys() {1252valMap.SetMapIndex(k, vMap.MapIndex(k))1253}1254} else {1255valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)1256}12571258case reflect.Slice:1259if deep {1260var childType reflect.Type1261switch v.Type().Elem().Kind() {1262case reflect.Struct:1263childType = reflect.TypeOf(map[string]any{})1264default:1265childType = v.Type().Elem()1266}12671268sType := reflect.SliceOf(childType)12691270addrVal := reflect.New(sType)12711272vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap())12731274if v.Len() > 0 {1275reflect.Indirect(addrVal).Set(vSlice)12761277err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal))1278if err != nil {1279return err1280}1281}12821283vSlice = reflect.Indirect(addrVal)12841285valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice)12861287break1288}12891290// When deep mapping is not needed, fallthrough to normal copy1291fallthrough12921293default:1294valMap.SetMapIndex(reflect.ValueOf(keyName), v)1295}1296}12971298if val.CanAddr() {1299val.Set(valMap)1300}13011302return nil1303}13041305func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) {1306// If the input data is nil, then we want to just set the output1307// pointer to be nil as well.1308isNil := data == nil1309if !isNil {1310switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {1311case reflect.Chan,1312reflect.Func,1313reflect.Interface,1314reflect.Map,1315reflect.Ptr,1316reflect.Slice:1317isNil = v.IsNil()1318}1319}1320if isNil {1321if !val.IsNil() && val.CanSet() {1322nilValue := reflect.New(val.Type()).Elem()1323val.Set(nilValue)1324}13251326return true, nil1327}13281329// Create an element of the concrete (non pointer) type and decode1330// into that. Then set the value of the pointer to this type.1331valType := val.Type()1332valElemType := valType.Elem()1333if val.CanSet() {1334realVal := val1335if realVal.IsNil() || d.config.ZeroFields {1336realVal = reflect.New(valElemType)1337}13381339if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {1340return false, err1341}13421343val.Set(realVal)1344} else {1345if err := d.decode(name, data, reflect.Indirect(val)); err != nil {1346return false, err1347}1348}1349return false, nil1350}13511352func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error {1353// Create an element of the concrete (non pointer) type and decode1354// into that. Then set the value of the pointer to this type.1355dataVal := reflect.Indirect(reflect.ValueOf(data))1356if val.Type() != dataVal.Type() {1357return newDecodeError(name, &UnconvertibleTypeError{1358Expected: val,1359Value: data,1360})1361}1362val.Set(dataVal)1363return nil1364}13651366func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {1367dataVal := reflect.Indirect(reflect.ValueOf(data))1368dataValKind := dataVal.Kind()1369valType := val.Type()1370valElemType := valType.Elem()1371sliceType := reflect.SliceOf(valElemType)13721373// If we have a non array/slice type then we first attempt to convert.1374if dataValKind != reflect.Array && dataValKind != reflect.Slice {1375if d.config.WeaklyTypedInput {1376switch {1377// Slice and array we use the normal logic1378case dataValKind == reflect.Slice, dataValKind == reflect.Array:1379break13801381// Empty maps turn into empty slices1382case dataValKind == reflect.Map:1383if dataVal.Len() == 0 {1384val.Set(reflect.MakeSlice(sliceType, 0, 0))1385return nil1386}1387// Create slice of maps of other sizes1388return d.decodeSlice(name, []any{data}, val)13891390case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:1391return d.decodeSlice(name, []byte(dataVal.String()), val)13921393// All other types we try to convert to the slice type1394// and "lift" it into it. i.e. a string becomes a string slice.1395default:1396// Just re-try this function with data as a slice.1397return d.decodeSlice(name, []any{data}, val)1398}1399}14001401return newDecodeError(name,1402fmt.Errorf("source data must be an array or slice, got %s", dataValKind))1403}14041405// If the input value is nil, then don't allocate since empty != nil1406if dataValKind != reflect.Array && dataVal.IsNil() {1407return nil1408}14091410valSlice := val1411if valSlice.IsNil() || d.config.ZeroFields {1412// Make a new slice to hold our result, same size as the original data.1413valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())1414} else if valSlice.Len() > dataVal.Len() {1415valSlice = valSlice.Slice(0, dataVal.Len())1416}14171418// Accumulate any errors1419var errs []error14201421for i := 0; i < dataVal.Len(); i++ {1422currentData := dataVal.Index(i).Interface()1423for valSlice.Len() <= i {1424valSlice = reflect.Append(valSlice, reflect.Zero(valElemType))1425}1426currentField := valSlice.Index(i)14271428fieldName := name + "[" + strconv.Itoa(i) + "]"1429if err := d.decode(fieldName, currentData, currentField); err != nil {1430errs = append(errs, err)1431}1432}14331434// Finally, set the value to the slice we built up1435val.Set(valSlice)14361437return errors.Join(errs...)1438}14391440func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error {1441dataVal := reflect.Indirect(reflect.ValueOf(data))1442dataValKind := dataVal.Kind()1443valType := val.Type()1444valElemType := valType.Elem()1445arrayType := reflect.ArrayOf(valType.Len(), valElemType)14461447valArray := val14481449if isComparable(valArray) && valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields {1450// Check input type1451if dataValKind != reflect.Array && dataValKind != reflect.Slice {1452if d.config.WeaklyTypedInput {1453switch {1454// Empty maps turn into empty arrays1455case dataValKind == reflect.Map:1456if dataVal.Len() == 0 {1457val.Set(reflect.Zero(arrayType))1458return nil1459}14601461// All other types we try to convert to the array type1462// and "lift" it into it. i.e. a string becomes a string array.1463default:1464// Just re-try this function with data as a slice.1465return d.decodeArray(name, []any{data}, val)1466}1467}14681469return newDecodeError(name,1470fmt.Errorf("source data must be an array or slice, got %s", dataValKind))14711472}1473if dataVal.Len() > arrayType.Len() {1474return newDecodeError(name,1475fmt.Errorf("expected source data to have length less or equal to %d, got %d", arrayType.Len(), dataVal.Len()))1476}14771478// Make a new array to hold our result, same size as the original data.1479valArray = reflect.New(arrayType).Elem()1480}14811482// Accumulate any errors1483var errs []error14841485for i := 0; i < dataVal.Len(); i++ {1486currentData := dataVal.Index(i).Interface()1487currentField := valArray.Index(i)14881489fieldName := name + "[" + strconv.Itoa(i) + "]"1490if err := d.decode(fieldName, currentData, currentField); err != nil {1491errs = append(errs, err)1492}1493}14941495// Finally, set the value to the array we built up1496val.Set(valArray)14971498return errors.Join(errs...)1499}15001501func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error {1502dataVal := reflect.Indirect(reflect.ValueOf(data))15031504// If the type of the value to write to and the data match directly,1505// then we just set it directly instead of recursing into the structure.1506if dataVal.Type() == val.Type() {1507val.Set(dataVal)1508return nil1509}15101511dataValKind := dataVal.Kind()1512switch dataValKind {1513case reflect.Map:1514return d.decodeStructFromMap(name, dataVal, val)15151516case reflect.Struct:1517// Not the most efficient way to do this but we can optimize later if1518// we want to. To convert from struct to struct we go to map first1519// as an intermediary.15201521// Make a new map to hold our result1522mapType := reflect.TypeOf((map[string]any)(nil))1523mval := reflect.MakeMap(mapType)15241525// Creating a pointer to a map so that other methods can completely1526// overwrite the map if need be (looking at you decodeMapFromMap). The1527// indirection allows the underlying map to be settable (CanSet() == true)1528// where as reflect.MakeMap returns an unsettable map.1529addrVal := reflect.New(mval.Type())15301531reflect.Indirect(addrVal).Set(mval)1532if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil {1533return err1534}15351536result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)1537return result15381539default:1540return newDecodeError(name,1541fmt.Errorf("expected a map or struct, got %q", dataValKind))1542}1543}15441545func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {1546dataValType := dataVal.Type()1547if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {1548return newDecodeError(name,1549fmt.Errorf("needs a map with string keys, has %q keys", kind))1550}15511552dataValKeys := make(map[reflect.Value]struct{})1553dataValKeysUnused := make(map[any]struct{})1554for _, dataValKey := range dataVal.MapKeys() {1555dataValKeys[dataValKey] = struct{}{}1556dataValKeysUnused[dataValKey.Interface()] = struct{}{}1557}15581559targetValKeysUnused := make(map[any]struct{})15601561var errs []error15621563// This slice will keep track of all the structs we'll be decoding.1564// There can be more than one struct if there are embedded structs1565// that are squashed.1566structs := make([]reflect.Value, 1, 5)1567structs[0] = val15681569// Compile the list of all the fields that we're going to be decoding1570// from all the structs.1571type field struct {1572field reflect.StructField1573val reflect.Value1574}15751576// remainField is set to a valid field set with the "remain" tag if1577// we are keeping track of remaining values.1578var remainField *field15791580fields := []field{}1581for len(structs) > 0 {1582structVal := structs[0]1583structs = structs[1:]15841585structType := structVal.Type()15861587for i := 0; i < structType.NumField(); i++ {1588fieldType := structType.Field(i)1589fieldVal := structVal.Field(i)1590if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {1591// Handle embedded struct pointers as embedded structs.1592fieldVal = fieldVal.Elem()1593}15941595// If "squash" is specified in the tag, we squash the field down.1596squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous1597remain := false15981599// We always parse the tags cause we're looking for other tags too1600tagParts := getTagParts(fieldType, d.config.TagName)1601if len(tagParts) == 0 {1602tagParts = []string{""}1603}1604for _, tag := range tagParts[1:] {1605if tag == d.config.SquashTagOption {1606squash = true1607break1608}16091610if tag == "remain" {1611remain = true1612break1613}1614}16151616if squash {1617switch fieldVal.Kind() {1618case reflect.Struct:1619structs = append(structs, fieldVal)1620case reflect.Interface:1621if !fieldVal.IsNil() {1622structs = append(structs, fieldVal.Elem().Elem())1623}1624case reflect.Ptr:1625if fieldVal.Type().Elem().Kind() == reflect.Struct {1626if fieldVal.IsNil() {1627fieldVal.Set(reflect.New(fieldVal.Type().Elem()))1628}1629structs = append(structs, fieldVal.Elem())1630} else {1631errs = append(errs, newDecodeError(1632name+"."+fieldType.Name,1633fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()),1634))1635}1636default:1637errs = append(errs, newDecodeError(1638name+"."+fieldType.Name,1639fmt.Errorf("unsupported type for squash: %s", fieldVal.Kind()),1640))1641}1642continue1643}16441645// Build our field1646if remain {1647remainField = &field{fieldType, fieldVal}1648} else {1649// Normal struct field, store it away1650fields = append(fields, field{fieldType, fieldVal})1651}1652}1653}16541655// for fieldType, field := range fields {1656for _, f := range fields {1657field, fieldValue := f.field, f.val1658fieldName := field.Name16591660tagValue, _ := getTagValue(field, d.config.TagName)1661if tagValue == "" && d.config.IgnoreUntaggedFields {1662continue1663}1664tagValue = strings.SplitN(tagValue, ",", 2)[0]1665if tagValue != "" {1666fieldName = tagValue1667} else {1668fieldName = d.config.MapFieldName(fieldName)1669}16701671rawMapKey := reflect.ValueOf(fieldName)1672rawMapVal := dataVal.MapIndex(rawMapKey)1673if !rawMapVal.IsValid() {1674// Do a slower search by iterating over each key and1675// doing case-insensitive search.1676for dataValKey := range dataValKeys {1677mK, ok := dataValKey.Interface().(string)1678if !ok {1679// Not a string key1680continue1681}16821683if d.config.MatchName(mK, fieldName) {1684rawMapKey = dataValKey1685rawMapVal = dataVal.MapIndex(dataValKey)1686break1687}1688}16891690if !rawMapVal.IsValid() {1691// There was no matching key in the map for the value in1692// the struct. Remember it for potential errors and metadata.1693if !(d.config.AllowUnsetPointer && fieldValue.Kind() == reflect.Ptr) {1694targetValKeysUnused[fieldName] = struct{}{}1695}1696continue1697}1698}16991700if !fieldValue.IsValid() {1701// This should never happen1702panic("field is not valid")1703}17041705// If we can't set the field, then it is unexported or something,1706// and we just continue onwards.1707if !fieldValue.CanSet() {1708continue1709}17101711// Delete the key we're using from the unused map so we stop tracking1712delete(dataValKeysUnused, rawMapKey.Interface())17131714// If the name is empty string, then we're at the root, and we1715// don't dot-join the fields.1716if name != "" {1717fieldName = name + "." + fieldName1718}17191720if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {1721errs = append(errs, err)1722}1723}17241725// If we have a "remain"-tagged field and we have unused keys then1726// we put the unused keys directly into the remain field.1727if remainField != nil && len(dataValKeysUnused) > 0 {1728// Build a map of only the unused values1729remain := map[any]any{}1730for key := range dataValKeysUnused {1731remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()1732}17331734// Decode it as-if we were just decoding this map onto our map.1735if err := d.decodeMap(name, remain, remainField.val); err != nil {1736errs = append(errs, err)1737}17381739// Set the map to nil so we have none so that the next check will1740// not error (ErrorUnused)1741dataValKeysUnused = nil1742}17431744if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {1745keys := make([]string, 0, len(dataValKeysUnused))1746for rawKey := range dataValKeysUnused {1747keys = append(keys, rawKey.(string))1748}1749sort.Strings(keys)17501751// Improve error message when name is empty by showing the target struct type1752// in the case where it is empty for embedded structs.1753errorName := name1754if errorName == "" {1755errorName = val.Type().String()1756}1757errs = append(errs, newDecodeError(1758errorName,1759fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),1760))1761}17621763if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {1764keys := make([]string, 0, len(targetValKeysUnused))1765for rawKey := range targetValKeysUnused {1766keys = append(keys, rawKey.(string))1767}1768sort.Strings(keys)17691770errs = append(errs, newDecodeError(1771name,1772fmt.Errorf("has unset fields: %s", strings.Join(keys, ", ")),1773))1774}17751776if err := errors.Join(errs...); err != nil {1777return err1778}17791780// Add the unused keys to the list of unused keys if we're tracking metadata1781if d.config.Metadata != nil {1782for rawKey := range dataValKeysUnused {1783key := rawKey.(string)1784if name != "" {1785key = name + "." + key1786}17871788d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)1789}1790for rawKey := range targetValKeysUnused {1791key := rawKey.(string)1792if name != "" {1793key = name + "." + key1794}17951796d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)1797}1798}17991800return nil1801}18021803func isEmptyValue(v reflect.Value) bool {1804switch getKind(v) {1805case reflect.Array, reflect.Map, reflect.Slice, reflect.String:1806return v.Len() == 01807case reflect.Bool:1808return !v.Bool()1809case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:1810return v.Int() == 01811case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:1812return v.Uint() == 01813case reflect.Float32, reflect.Float64:1814return v.Float() == 01815case reflect.Interface, reflect.Ptr:1816return v.IsNil()1817}1818return false1819}18201821func getKind(val reflect.Value) reflect.Kind {1822kind := val.Kind()18231824switch {1825case kind >= reflect.Int && kind <= reflect.Int64:1826return reflect.Int1827case kind >= reflect.Uint && kind <= reflect.Uint64:1828return reflect.Uint1829case kind >= reflect.Float32 && kind <= reflect.Float64:1830return reflect.Float321831case kind >= reflect.Complex64 && kind <= reflect.Complex128:1832return reflect.Complex641833default:1834return kind1835}1836}18371838func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {1839for i := 0; i < typ.NumField(); i++ {1840f := typ.Field(i)1841if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields1842return true1843}1844if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside1845return true1846}1847}1848return false1849}18501851func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {1852if v.Kind() != reflect.Ptr {1853return v1854}18551856switch v.Elem().Kind() {1857case reflect.Slice:1858return v.Elem()18591860case reflect.Struct:1861deref := v.Elem()1862derefT := deref.Type()1863if isStructTypeConvertibleToMap(derefT, true, tagName) {1864return deref1865}1866return v18671868default:1869return v1870}1871}18721873func hasAnyTag(field reflect.StructField, tagName string) bool {1874_, ok := getTagValue(field, tagName)1875return ok1876}18771878func getTagParts(field reflect.StructField, tagName string) []string {1879tagValue, ok := getTagValue(field, tagName)1880if !ok {1881return nil1882}1883return strings.Split(tagValue, ",")1884}18851886func getTagValue(field reflect.StructField, tagName string) (string, bool) {1887for _, name := range splitTagNames(tagName) {1888if tag := field.Tag.Get(name); tag != "" {1889return tag, true1890}1891}1892return "", false1893}18941895func splitTagNames(tagName string) []string {1896if tagName == "" {1897return []string{"mapstructure"}1898}1899parts := strings.Split(tagName, ",")1900result := make([]string, 0, len(parts))19011902for _, name := range parts {1903name = strings.TrimSpace(name)1904if name != "" {1905result = append(result, name)1906}1907}19081909return result1910}19111912// unmarshalerType is cached for performance1913var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()19141915// getUnmarshaler checks if the value implements Unmarshaler and returns1916// the Unmarshaler and a boolean indicating if it was found. It handles both1917// pointer and value receivers.1918func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) {1919// Skip invalid or nil values1920if !val.IsValid() {1921return nil, false1922}19231924switch val.Kind() {1925case reflect.Pointer, reflect.Interface:1926if val.IsNil() {1927return nil, false1928}1929}19301931// Check pointer receiver first (most common case)1932if val.CanAddr() {1933ptrVal := val.Addr()1934// Quick check: if no methods, can't implement any interface1935if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) {1936return ptrVal.Interface().(Unmarshaler), true1937}1938}19391940// Check value receiver1941// Quick check: if no methods, can't implement any interface1942if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) {1943return val.Interface().(Unmarshaler), true1944}19451946return nil, false1947}194819491950