Skip to content

Commit

Permalink
feat: variation of PR mitchellh#294
Browse files Browse the repository at this point in the history
  • Loading branch information
pcfreak30 committed Jun 3, 2024
1 parent cb699d2 commit c63fee0
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 6 deletions.
23 changes: 17 additions & 6 deletions mapstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// into a native Go structure.
//
// The Go structure can be arbitrarily complex, containing slices,
// other structs, etc. and the decoder will properly decode nested
// other structs, etc. and the Unmarshaler will properly decode nested
// maps and so on into the proper structures in the native Go struct.
// See the examples to see what the decoder is capable of.
// See the examples to see what the Unmarshaler is capable of.
//
// The simplest function to start with is Decode.
//
Expand Down Expand Up @@ -129,7 +129,7 @@
// # Unexported fields
//
// 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 Unmarshaler will simply skip them.
//
// For this output type definition:
//
Expand Down Expand Up @@ -196,7 +196,7 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface
// values.
type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)

// DecoderConfig is the configuration that is used to create a new decoder
// DecoderConfig is the configuration that is used to create a new Unmarshaler
// and allows customization of various aspects of decoding.
type DecoderConfig struct {
// DecodeHook, if set, will be called before any decoding and any
Expand Down Expand Up @@ -368,8 +368,8 @@ func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadat
return decoder.Decode(input)
}

// NewDecoder returns a new decoder for the given configuration. Once
// a decoder has been returned, the same configuration must not be used
// NewDecoder returns a new Unmarshaler for the given configuration. Once
// a Unmarshaler has been returned, the same configuration must not be used
// again.
func NewDecoder(config *DecoderConfig) (*Decoder, error) {
val := reflect.ValueOf(config.Result)
Expand Down Expand Up @@ -462,6 +462,13 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
}
}

if outVal.CanAddr() {
v := outVal.Addr()
if u, ok := v.Interface().(Unmarshaler); ok {
return u.DecodeMapstructure(input)
}
}

var err error
outputKind := getKind(outVal)
addMetaKey := true
Expand Down Expand Up @@ -1580,3 +1587,7 @@ func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Val
}
return v
}

type Unmarshaler interface {
DecodeMapstructure(interface{}) error
}
29 changes: 29 additions & 0 deletions mapstructure_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,35 @@ func ExampleDecode_omitempty() {
// &map[Age:0 FirstName:Somebody]
}

type Someone struct {
Name string
}

func (p *Someone) DecodeMapstructure(v interface{}) error {
if name, ok := v.(string); ok {
p.Name = name
return nil
}
return fmt.Errorf("undecipherable name")
}

func ExampleDecode_custom_decoder_type() {
type Profile struct {
Friends []Someone
}
input := map[string]interface{}{
"friends": []string{"Mitchell"},
}
var result Profile
err := Decode(input, &result)
if err != nil {
panic(err)
}
fmt.Printf("%#v", result)
// Output:
// mapstructure.Profile{Friends:[]mapstructure.Someone{mapstructure.Someone{Name:"Mitchell"}}}
}

func ExampleDecode_decodeHookFunc() {
type PersonLocation struct {
Latitude float64
Expand Down

0 comments on commit c63fee0

Please sign in to comment.