opentracing -> opencensus (#802)

* update vendor directory, add go.opencensus.io

* update imports

* oops

* s/opentracing/opencensus/ & remove prometheus / zipkin stuff & remove old stats

* the dep train rides again

* fix gin build

* deps from last guy

* start in on the agent metrics

* she builds

* remove tags for now, cardinality error is fussing. subscribe instead of register

* update to patched version of opencensus to proceed for now TODO switch to a release

* meh

fix imports

* println debug the bad boys

* lace it with the tags

* update deps again

* fix all inconsistent cardinality errors

* add our own logger

* fix init

* fix oom measure

* remove bugged removal code

* fix s3 measures

* fix prom handler nil
This commit is contained in:
Reed Allman
2018-03-05 09:35:28 -08:00
committed by GitHub
parent 924d27559c
commit 206aa3c203
5975 changed files with 158755 additions and 566592 deletions

View File

@@ -1,4 +1,4 @@
# mapstructure [![Godoc](https://godoc.org/github.com/mitchell/mapstructure?status.svg)](https://godoc.org/github.com/mitchell/mapstructure)
# 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.

View File

@@ -115,6 +115,25 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
}
}
// 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.
//

View File

@@ -153,6 +153,36 @@ func TestStringToTimeDurationHookFunc(t *testing.T) {
}
}
func TestStringToTimeHookFunc(t *testing.T) {
strType := reflect.TypeOf("")
timeType := reflect.TypeOf(time.Time{})
cases := []struct {
f, t reflect.Type
layout string
data interface{}
result interface{}
err bool
}{
{strType, timeType, time.RFC3339, "2006-01-02T15:04:05Z",
time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
{strType, timeType, time.RFC3339, "5", time.Time{}, true},
{strType, strType, time.RFC3339, "5", "5", false},
}
for i, tc := range cases {
f := StringToTimeHookFunc(tc.layout)
actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
if tc.err != (err != nil) {
t.Fatalf("case %d: expected err %#v", i, tc.err)
}
if !reflect.DeepEqual(actual, tc.result) {
t.Fatalf(
"case %d: expected %#v, got %#v",
i, tc.result, actual)
}
}
}
func TestWeaklyTypedHook(t *testing.T) {
var f DecodeHookFunc = WeaklyTypedHook

View File

@@ -114,12 +114,12 @@ type Metadata struct {
Unused []string
}
// Decode takes a map and uses reflection to convert it into the
// given Go native structure. val must be a pointer to a struct.
func Decode(m interface{}, rawVal interface{}) error {
// Decode takes an input structure and uses reflection to translate it to
// the output structure. output must be a pointer to a map or struct.
func Decode(input interface{}, output interface{}) error {
config := &DecoderConfig{
Metadata: nil,
Result: rawVal,
Result: output,
}
decoder, err := NewDecoder(config)
@@ -127,7 +127,7 @@ func Decode(m interface{}, rawVal interface{}) error {
return err
}
return decoder.Decode(m)
return decoder.Decode(input)
}
// WeakDecode is the same as Decode but is shorthand to enable
@@ -147,6 +147,40 @@ func WeakDecode(input, output interface{}) error {
return decoder.Decode(input)
}
// DecodeMetadata is the same as Decode, but is shorthand to
// enable metadata collection. See DecoderConfig for more info.
func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
config := &DecoderConfig{
Metadata: metadata,
Result: output,
}
decoder, err := NewDecoder(config)
if err != nil {
return err
}
return decoder.Decode(input)
}
// WeakDecodeMetadata is the same as Decode, but is shorthand to
// enable both WeaklyTypedInput and metadata collection. See
// DecoderConfig for more info.
func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
config := &DecoderConfig{
Metadata: metadata,
Result: output,
WeaklyTypedInput: true,
}
decoder, err := NewDecoder(config)
if err != nil {
return err
}
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
// again.
@@ -184,70 +218,81 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
// Decode decodes the given raw interface to the target pointer specified
// by the configuration.
func (d *Decoder) Decode(raw interface{}) error {
return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem())
func (d *Decoder) Decode(input interface{}) error {
return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
}
// Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error {
if data == nil {
// If the data is nil, then we don't set anything.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
if input == nil {
// If the data is nil, then we don't set anything, unless ZeroFields is set
// to true.
if d.config.ZeroFields {
outVal.Set(reflect.Zero(outVal.Type()))
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
}
return nil
}
dataVal := reflect.ValueOf(data)
if !dataVal.IsValid() {
// If the data value is invalid, then we just set the value
inputVal := reflect.ValueOf(input)
if !inputVal.IsValid() {
// If the input value is invalid, then we just set the value
// to be the zero value.
val.Set(reflect.Zero(val.Type()))
outVal.Set(reflect.Zero(outVal.Type()))
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
return nil
}
if d.config.DecodeHook != nil {
// We have a DecodeHook, so let's pre-process the data.
// We have a DecodeHook, so let's pre-process the input.
var err error
data, err = DecodeHookExec(
input, err = DecodeHookExec(
d.config.DecodeHook,
dataVal.Type(), val.Type(), data)
inputVal.Type(), outVal.Type(), input)
if err != nil {
return fmt.Errorf("error decoding '%s': %s", name, err)
}
}
var err error
dataKind := getKind(val)
switch dataKind {
inputKind := getKind(outVal)
switch inputKind {
case reflect.Bool:
err = d.decodeBool(name, data, val)
err = d.decodeBool(name, input, outVal)
case reflect.Interface:
err = d.decodeBasic(name, data, val)
err = d.decodeBasic(name, input, outVal)
case reflect.String:
err = d.decodeString(name, data, val)
err = d.decodeString(name, input, outVal)
case reflect.Int:
err = d.decodeInt(name, data, val)
err = d.decodeInt(name, input, outVal)
case reflect.Uint:
err = d.decodeUint(name, data, val)
err = d.decodeUint(name, input, outVal)
case reflect.Float32:
err = d.decodeFloat(name, data, val)
err = d.decodeFloat(name, input, outVal)
case reflect.Struct:
err = d.decodeStruct(name, data, val)
err = d.decodeStruct(name, input, outVal)
case reflect.Map:
err = d.decodeMap(name, data, val)
err = d.decodeMap(name, input, outVal)
case reflect.Ptr:
err = d.decodePtr(name, data, val)
err = d.decodePtr(name, input, outVal)
case reflect.Slice:
err = d.decodeSlice(name, data, val)
err = d.decodeSlice(name, input, outVal)
case reflect.Array:
err = d.decodeArray(name, data, val)
err = d.decodeArray(name, input, outVal)
case reflect.Func:
err = d.decodeFunc(name, data, val)
err = d.decodeFunc(name, input, outVal)
default:
// If we reached this point then we weren't able to decode it
return fmt.Errorf("%s: unsupported type: %s", name, dataKind)
return fmt.Errorf("%s: unsupported type: %s", name, inputKind)
}
// If we reached here, then we successfully decoded SOMETHING, so
// mark the key as used if we're tracking metadata.
// mark the key as used if we're tracking metainput.
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
@@ -258,6 +303,9 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
// This decodes a basic type (bool, int, string, etc.) and sets the
// value to "data" of that type.
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
if val.IsValid() && val.Elem().IsValid() {
return d.decode(name, data, val.Elem())
}
dataVal := reflect.ValueOf(data)
if !dataVal.IsValid() {
dataVal = reflect.Zero(val.Type())
@@ -499,34 +547,50 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
valMap = reflect.MakeMap(mapType)
}
// Check input type
// Check input type and based on the input type jump to the proper func
dataVal := reflect.Indirect(reflect.ValueOf(data))
if dataVal.Kind() != reflect.Map {
// In weak mode, we accept a slice of maps as an input...
switch dataVal.Kind() {
case reflect.Map:
return d.decodeMapFromMap(name, dataVal, val, valMap)
case reflect.Struct:
return d.decodeMapFromStruct(name, dataVal, val, valMap)
case reflect.Array, reflect.Slice:
if d.config.WeaklyTypedInput {
switch dataVal.Kind() {
case reflect.Array, reflect.Slice:
// Special case for BC reasons (covered by tests)
if dataVal.Len() == 0 {
val.Set(valMap)
return nil
}
for i := 0; i < dataVal.Len(); i++ {
err := d.decode(
fmt.Sprintf("%s[%d]", name, i),
dataVal.Index(i).Interface(), val)
if err != nil {
return err
}
}
return nil
}
return d.decodeMapFromSlice(name, dataVal, val, valMap)
}
fallthrough
default:
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
}
}
func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
// Special case for BC reasons (covered by tests)
if dataVal.Len() == 0 {
val.Set(valMap)
return nil
}
for i := 0; i < dataVal.Len(); i++ {
err := d.decode(
fmt.Sprintf("%s[%d]", name, i),
dataVal.Index(i).Interface(), val)
if err != nil {
return err
}
}
return nil
}
func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
valType := val.Type()
valKeyType := valType.Key()
valElemType := valType.Elem()
// Accumulate errors
errors := make([]string, 0)
@@ -563,22 +627,88 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
return nil
}
func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
typ := dataVal.Type()
for i := 0; i < typ.NumField(); i++ {
// Get the StructField first since this is a cheap operation. If the
// field is unexported, then ignore it.
f := typ.Field(i)
if f.PkgPath != "" {
continue
}
// Next get the actual value of this field and verify it is assignable
// to the map value.
v := dataVal.Field(i)
if !v.Type().AssignableTo(valMap.Type().Elem()) {
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
}
// Determine the name of the key in the map
keyName := f.Name
tagValue := f.Tag.Get(d.config.TagName)
tagValue = strings.SplitN(tagValue, ",", 2)[0]
if tagValue != "" {
if tagValue == "-" {
continue
}
keyName = tagValue
}
switch v.Kind() {
// this is an embedded struct, so handle it differently
case reflect.Struct:
x := reflect.New(v.Type())
x.Elem().Set(v)
vType := valMap.Type()
vKeyType := vType.Key()
vElemType := vType.Elem()
mType := reflect.MapOf(vKeyType, vElemType)
vMap := reflect.MakeMap(mType)
err := d.decode(keyName, x.Interface(), vMap)
if err != nil {
return err
}
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
default:
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
}
}
if val.CanAddr() {
val.Set(valMap)
}
return nil
}
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
// Create an element of the concrete (non pointer) type and decode
// into that. Then set the value of the pointer to this type.
valType := val.Type()
valElemType := valType.Elem()
realVal := val
if realVal.IsNil() || d.config.ZeroFields {
realVal = reflect.New(valElemType)
}
if val.CanSet() {
realVal := val
if realVal.IsNil() || d.config.ZeroFields {
realVal = reflect.New(valElemType)
}
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
return err
}
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
return err
}
val.Set(realVal)
val.Set(realVal)
} else {
if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
return err
}
}
return nil
}
@@ -614,7 +744,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
val.Set(reflect.MakeSlice(sliceType, 0, 0))
return nil
}
case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
return d.decodeSlice(name, []byte(dataVal.String()), val)
// All other types we try to convert to the slice type
// and "lift" it into it. i.e. a string becomes a string slice.
default:
@@ -622,7 +753,6 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
return d.decodeSlice(name, []interface{}{data}, val)
}
}
return fmt.Errorf(
"'%s': source data must be an array or slice, got %s", name, dataValKind)

View File

@@ -1,48 +1,142 @@
package mapstructure
import "testing"
import (
"reflect"
"testing"
)
// GH-1
// GH-1, GH-10, GH-96
func TestDecode_NilValue(t *testing.T) {
input := map[string]interface{}{
"vfoo": nil,
"vother": nil,
t.Parallel()
tests := []struct {
name string
in interface{}
target interface{}
out interface{}
metaKeys []string
metaUnused []string
}{
{
"all nil",
&map[string]interface{}{
"vfoo": nil,
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{},
},
{
"partial nil",
&map[string]interface{}{
"vfoo": "baz",
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "baz", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{},
},
{
"partial decode",
&map[string]interface{}{
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "foo", Vother: nil},
[]string{"Vother"},
[]string{},
},
{
"unused values",
&map[string]interface{}{
"vbar": "bar",
"vfoo": nil,
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{"vbar"},
},
{
"map interface all nil",
&map[interface{}]interface{}{
"vfoo": nil,
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{},
},
{
"map interface partial nil",
&map[interface{}]interface{}{
"vfoo": "baz",
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "baz", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{},
},
{
"map interface partial decode",
&map[interface{}]interface{}{
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "foo", Vother: nil},
[]string{"Vother"},
[]string{},
},
{
"map interface unused values",
&map[interface{}]interface{}{
"vbar": "bar",
"vfoo": nil,
"vother": nil,
},
&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
&Map{Vfoo: "", Vother: nil},
[]string{"Vfoo", "Vother"},
[]string{"vbar"},
},
}
var result Map
err := Decode(input, &result)
if err != nil {
t.Fatalf("should not error: %s", err)
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
config := &DecoderConfig{
Metadata: new(Metadata),
Result: tc.target,
ZeroFields: true,
}
if result.Vfoo != "" {
t.Fatalf("value should be default: %s", result.Vfoo)
}
decoder, err := NewDecoder(config)
if err != nil {
t.Fatalf("should not error: %s", err)
}
if result.Vother != nil {
t.Fatalf("Vother should be nil: %s", result.Vother)
}
}
err = decoder.Decode(tc.in)
if err != nil {
t.Fatalf("should not error: %s", err)
}
// GH-10
func TestDecode_mapInterfaceInterface(t *testing.T) {
input := map[interface{}]interface{}{
"vfoo": nil,
"vother": nil,
}
if !reflect.DeepEqual(tc.out, tc.target) {
t.Fatalf("%q: TestDecode_NilValue() expected: %#v, got: %#v", tc.name, tc.out, tc.target)
}
var result Map
err := Decode(input, &result)
if err != nil {
t.Fatalf("should not error: %s", err)
}
if !reflect.DeepEqual(tc.metaKeys, config.Metadata.Keys) {
t.Fatalf("%q: Metadata.Keys mismatch expected: %#v, got: %#v", tc.name, tc.metaKeys, config.Metadata.Keys)
}
if result.Vfoo != "" {
t.Fatalf("value should be default: %s", result.Vfoo)
}
if result.Vother != nil {
t.Fatalf("Vother should be nil: %s", result.Vother)
if !reflect.DeepEqual(tc.metaUnused, config.Metadata.Unused) {
t.Fatalf("%q: Metadata.Unused mismatch expected: %#v, got: %#v", tc.name, tc.metaUnused, config.Metadata.Unused)
}
})
}
}

View File

@@ -128,6 +128,7 @@ type TypeConversionResult struct {
FloatToBool bool
FloatToString string
SliceUint8ToString string
StringToSliceUint8 []byte
ArrayUint8ToString string
StringToInt int
StringToUint uint
@@ -248,6 +249,32 @@ func TestBasic_Merge(t *testing.T) {
}
}
// Test for issue #46.
func TestBasic_Struct(t *testing.T) {
t.Parallel()
input := map[string]interface{}{
"vdata": map[string]interface{}{
"vstring": "foo",
},
}
var result, inner Basic
result.Vdata = &inner
err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err)
}
expected := Basic{
Vdata: &Basic{
Vstring: "foo",
},
}
if !reflect.DeepEqual(result, expected) {
t.Fatalf("bad: %#v", result)
}
}
func TestDecode_BasicSquash(t *testing.T) {
t.Parallel()
@@ -626,6 +653,7 @@ func TestDecode_TypeConversion(t *testing.T) {
"FloatToBool": 42.42,
"FloatToString": 42.42,
"SliceUint8ToString": []uint8("foo"),
"StringToSliceUint8": "foo",
"ArrayUint8ToString": [3]uint8{'f', 'o', 'o'},
"StringToInt": "42",
"StringToUint": "42",
@@ -671,6 +699,7 @@ func TestDecode_TypeConversion(t *testing.T) {
FloatToBool: true,
FloatToString: "42.42",
SliceUint8ToString: "foo",
StringToSliceUint8: []byte("foo"),
ArrayUint8ToString: "foo",
StringToInt: 42,
StringToUint: 42,
@@ -926,6 +955,56 @@ func TestNestedTypePointer(t *testing.T) {
}
}
// Test for issue #46.
func TestNestedTypeInterface(t *testing.T) {
t.Parallel()
input := map[string]interface{}{
"vfoo": "foo",
"vbar": &map[string]interface{}{
"vstring": "foo",
"vint": 42,
"vbool": true,
"vdata": map[string]interface{}{
"vstring": "bar",
},
},
}
var result NestedPointer
result.Vbar = new(Basic)
result.Vbar.Vdata = new(Basic)
err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err.Error())
}
if result.Vfoo != "foo" {
t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
}
if result.Vbar.Vstring != "foo" {
t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
}
if result.Vbar.Vint != 42 {
t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
}
if result.Vbar.Vbool != true {
t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
}
if result.Vbar.Vextra != "" {
t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
}
if result.Vbar.Vdata.(*Basic).Vstring != "bar" {
t.Errorf("vstring value should be 'bar': %#v", result.Vbar.Vdata.(*Basic).Vstring)
}
}
func TestSlice(t *testing.T) {
t.Parallel()
@@ -1112,6 +1191,197 @@ func TestArrayToMap(t *testing.T) {
}
}
func TestMapOutputForStructuredInputs(t *testing.T) {
t.Parallel()
tests := []struct {
name string
in interface{}
target interface{}
out interface{}
wantErr bool
}{
{
"basic struct input",
&Basic{
Vstring: "vstring",
Vint: 2,
Vuint: 3,
Vbool: true,
Vfloat: 4.56,
Vextra: "vextra",
vsilent: true,
Vdata: []byte("data"),
},
&map[string]interface{}{},
&map[string]interface{}{
"Vstring": "vstring",
"Vint": 2,
"Vuint": uint(3),
"Vbool": true,
"Vfloat": 4.56,
"Vextra": "vextra",
"Vdata": []byte("data"),
"VjsonInt": 0,
"VjsonFloat": 0.0,
"VjsonNumber": json.Number(""),
},
false,
},
{
"embedded struct input",
&Embedded{
Vunique: "vunique",
Basic: Basic{
Vstring: "vstring",
Vint: 2,
Vuint: 3,
Vbool: true,
Vfloat: 4.56,
Vextra: "vextra",
vsilent: true,
Vdata: []byte("data"),
},
},
&map[string]interface{}{},
&map[string]interface{}{
"Vunique": "vunique",
"Basic": map[string]interface{}{
"Vstring": "vstring",
"Vint": 2,
"Vuint": uint(3),
"Vbool": true,
"Vfloat": 4.56,
"Vextra": "vextra",
"Vdata": []byte("data"),
"VjsonInt": 0,
"VjsonFloat": 0.0,
"VjsonNumber": json.Number(""),
},
},
false,
},
{
"slice input - should error",
[]string{"foo", "bar"},
&map[string]interface{}{},
&map[string]interface{}{},
true,
},
{
"struct with slice property",
&Slice{
Vfoo: "vfoo",
Vbar: []string{"foo", "bar"},
},
&map[string]interface{}{},
&map[string]interface{}{
"Vfoo": "vfoo",
"Vbar": []string{"foo", "bar"},
},
false,
},
{
"struct with slice of struct property",
&SliceOfStruct{
Value: []Basic{
Basic{
Vstring: "vstring",
Vint: 2,
Vuint: 3,
Vbool: true,
Vfloat: 4.56,
Vextra: "vextra",
vsilent: true,
Vdata: []byte("data"),
},
},
},
&map[string]interface{}{},
&map[string]interface{}{
"Value": []Basic{
Basic{
Vstring: "vstring",
Vint: 2,
Vuint: 3,
Vbool: true,
Vfloat: 4.56,
Vextra: "vextra",
vsilent: true,
Vdata: []byte("data"),
},
},
},
false,
},
{
"struct with map property",
&Map{
Vfoo: "vfoo",
Vother: map[string]string{"vother": "vother"},
},
&map[string]interface{}{},
&map[string]interface{}{
"Vfoo": "vfoo",
"Vother": map[string]string{
"vother": "vother",
}},
false,
},
{
"tagged struct",
&Tagged{
Extra: "extra",
Value: "value",
},
&map[string]string{},
&map[string]string{
"bar": "extra",
"foo": "value",
},
false,
},
{
"omit tag struct",
&struct {
Value string `mapstructure:"value"`
Omit string `mapstructure:"-"`
}{
Value: "value",
Omit: "omit",
},
&map[string]string{},
&map[string]string{
"value": "value",
},
false,
},
{
"decode to wrong map type",
&struct {
Value string
}{
Value: "string",
},
&map[string]int{},
&map[string]int{},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Decode(tt.in, tt.target); (err != nil) != tt.wantErr {
t.Fatalf("%q: TestMapOutputForStructuredInputs() unexpected error: %s", tt.name, err)
}
if !reflect.DeepEqual(tt.out, tt.target) {
t.Fatalf("%q: TestMapOutputForStructuredInputs() expected: %#v, got: %#v", tt.name, tt.out, tt.target)
}
})
}
}
func TestInvalidType(t *testing.T) {
t.Parallel()
@@ -1171,6 +1441,39 @@ func TestInvalidType(t *testing.T) {
}
}
func TestDecodeMetadata(t *testing.T) {
t.Parallel()
input := map[string]interface{}{
"vfoo": "foo",
"vbar": map[string]interface{}{
"vstring": "foo",
"Vuint": 42,
"foo": "bar",
},
"bar": "nil",
}
var md Metadata
var result Nested
err := DecodeMetadata(input, &result, &md)
if err != nil {
t.Fatalf("err: %s", err.Error())
}
expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"}
sort.Strings(md.Keys)
if !reflect.DeepEqual(md.Keys, expectedKeys) {
t.Fatalf("bad keys: %#v", md.Keys)
}
expectedUnused := []string{"Vbar.foo", "bar"}
if !reflect.DeepEqual(md.Unused, expectedUnused) {
t.Fatalf("bad unused: %#v", md.Unused)
}
}
func TestMetadata(t *testing.T) {
t.Parallel()
@@ -1311,6 +1614,43 @@ func TestWeakDecode(t *testing.T) {
}
}
func TestWeakDecodeMetadata(t *testing.T) {
t.Parallel()
input := map[string]interface{}{
"foo": "4",
"bar": "value",
"unused": "value",
}
var md Metadata
var result struct {
Foo int
Bar string
}
if err := WeakDecodeMetadata(input, &result, &md); err != nil {
t.Fatalf("err: %s", err)
}
if result.Foo != 4 {
t.Fatalf("bad: %#v", result)
}
if result.Bar != "value" {
t.Fatalf("bad: %#v", result)
}
expectedKeys := []string{"Bar", "Foo"}
sort.Strings(md.Keys)
if !reflect.DeepEqual(md.Keys, expectedKeys) {
t.Fatalf("bad keys: %#v", md.Keys)
}
expectedUnused := []string{"unused"}
if !reflect.DeepEqual(md.Unused, expectedUnused) {
t.Fatalf("bad unused: %#v", md.Unused)
}
}
func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
var result Slice
err := Decode(input, &result)