vendor/github.com/mitchellh/mapstructure/mapstructure.go
changeset 260 445e01aede7e
parent 256 6d9efbef00a9
equal deleted inserted replaced
259:db4911b0c721 260:445e01aede7e
   120 //
   120 //
   121 // For example, the zero type of a numeric type is zero ("0"). If the struct
   121 // For example, the zero type of a numeric type is zero ("0"). If the struct
   122 // field value is zero and a numeric type, the field is empty, and it won't
   122 // field value is zero and a numeric type, the field is empty, and it won't
   123 // be encoded into the destination type.
   123 // be encoded into the destination type.
   124 //
   124 //
   125 //     type Source {
   125 //     type Source struct {
   126 //         Age int `mapstructure:",omitempty"`
   126 //         Age int `mapstructure:",omitempty"`
   127 //     }
   127 //     }
   128 //
   128 //
   129 // Unexported fields
   129 // Unexported fields
   130 //
   130 //
   190 
   190 
   191 // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the
   191 // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the
   192 // source and target types.
   192 // source and target types.
   193 type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
   193 type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
   194 
   194 
   195 // DecodeHookFuncRaw is a DecodeHookFunc which has complete access to both the source and target
   195 // DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target
   196 // values.
   196 // values.
   197 type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
   197 type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
   198 
   198 
   199 // DecoderConfig is the configuration that is used to create a new decoder
   199 // DecoderConfig is the configuration that is used to create a new decoder
   200 // and allows customization of various aspects of decoding.
   200 // and allows customization of various aspects of decoding.
   212 
   212 
   213 	// If ErrorUnused is true, then it is an error for there to exist
   213 	// If ErrorUnused is true, then it is an error for there to exist
   214 	// keys in the original map that were unused in the decoding process
   214 	// keys in the original map that were unused in the decoding process
   215 	// (extra keys).
   215 	// (extra keys).
   216 	ErrorUnused bool
   216 	ErrorUnused bool
       
   217 
       
   218 	// If ErrorUnset is true, then it is an error for there to exist
       
   219 	// fields in the result that were not set in the decoding process
       
   220 	// (extra fields). This only applies to decoding to a struct. This
       
   221 	// will affect all nested structs as well.
       
   222 	ErrorUnset bool
   217 
   223 
   218 	// ZeroFields, if set to true, will zero fields before writing them.
   224 	// ZeroFields, if set to true, will zero fields before writing them.
   219 	// For example, a map will be emptied before decoded values are put in
   225 	// For example, a map will be emptied before decoded values are put in
   220 	// it. If this is false, a map will be merged.
   226 	// it. If this is false, a map will be merged.
   221 	ZeroFields bool
   227 	ZeroFields bool
   256 	Result interface{}
   262 	Result interface{}
   257 
   263 
   258 	// The tag name that mapstructure reads for field names. This
   264 	// The tag name that mapstructure reads for field names. This
   259 	// defaults to "mapstructure"
   265 	// defaults to "mapstructure"
   260 	TagName string
   266 	TagName string
       
   267 
       
   268 	// IgnoreUntaggedFields ignores all struct fields without explicit
       
   269 	// TagName, comparable to `mapstructure:"-"` as default behaviour.
       
   270 	IgnoreUntaggedFields bool
       
   271 
       
   272 	// MatchName is the function used to match the map key to the struct
       
   273 	// field name or tag. Defaults to `strings.EqualFold`. This can be used
       
   274 	// to implement case-sensitive tag values, support snake casing, etc.
       
   275 	MatchName func(mapKey, fieldName string) bool
   261 }
   276 }
   262 
   277 
   263 // A Decoder takes a raw interface value and turns it into structured
   278 // A Decoder takes a raw interface value and turns it into structured
   264 // data, keeping track of rich error information along the way in case
   279 // data, keeping track of rich error information along the way in case
   265 // anything goes wrong. Unlike the basic top-level Decode method, you can
   280 // anything goes wrong. Unlike the basic top-level Decode method, you can
   277 	Keys []string
   292 	Keys []string
   278 
   293 
   279 	// Unused is a slice of keys that were found in the raw value but
   294 	// Unused is a slice of keys that were found in the raw value but
   280 	// weren't decoded since there was no matching field in the result interface
   295 	// weren't decoded since there was no matching field in the result interface
   281 	Unused []string
   296 	Unused []string
       
   297 
       
   298 	// Unset is a slice of field names that were found in the result interface
       
   299 	// but weren't set in the decoding process since there was no matching value
       
   300 	// in the input
       
   301 	Unset []string
   282 }
   302 }
   283 
   303 
   284 // Decode takes an input structure and uses reflection to translate it to
   304 // Decode takes an input structure and uses reflection to translate it to
   285 // the output structure. output must be a pointer to a map or struct.
   305 // the output structure. output must be a pointer to a map or struct.
   286 func Decode(input interface{}, output interface{}) error {
   306 func Decode(input interface{}, output interface{}) error {
   368 		}
   388 		}
   369 
   389 
   370 		if config.Metadata.Unused == nil {
   390 		if config.Metadata.Unused == nil {
   371 			config.Metadata.Unused = make([]string, 0)
   391 			config.Metadata.Unused = make([]string, 0)
   372 		}
   392 		}
       
   393 
       
   394 		if config.Metadata.Unset == nil {
       
   395 			config.Metadata.Unset = make([]string, 0)
       
   396 		}
   373 	}
   397 	}
   374 
   398 
   375 	if config.TagName == "" {
   399 	if config.TagName == "" {
   376 		config.TagName = "mapstructure"
   400 		config.TagName = "mapstructure"
       
   401 	}
       
   402 
       
   403 	if config.MatchName == nil {
       
   404 		config.MatchName = strings.EqualFold
   377 	}
   405 	}
   378 
   406 
   379 	result := &Decoder{
   407 	result := &Decoder{
   380 		config: config,
   408 		config: config,
   381 	}
   409 	}
   673 		} else {
   701 		} else {
   674 			return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
   702 			return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
   675 		}
   703 		}
   676 	case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
   704 	case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
   677 		jn := data.(json.Number)
   705 		jn := data.(json.Number)
   678 		i, err := jn.Int64()
   706 		i, err := strconv.ParseUint(string(jn), 0, 64)
   679 		if err != nil {
   707 		if err != nil {
   680 			return fmt.Errorf(
   708 			return fmt.Errorf(
   681 				"error decoding json.Number into %s: %s", name, err)
   709 				"error decoding json.Number into %s: %s", name, err)
   682 		}
   710 		}
   683 		if i < 0 && !d.config.WeaklyTypedInput {
   711 		val.SetUint(i)
   684 			return fmt.Errorf("cannot parse '%s', %d overflows uint",
       
   685 				name, i)
       
   686 		}
       
   687 		val.SetUint(uint64(i))
       
   688 	default:
   712 	default:
   689 		return fmt.Errorf(
   713 		return fmt.Errorf(
   690 			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
   714 			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
   691 			name, val.Type(), dataVal.Type(), data)
   715 			name, val.Type(), dataVal.Type(), data)
   692 	}
   716 	}
   899 		}
   923 		}
   900 
   924 
   901 		tagValue := f.Tag.Get(d.config.TagName)
   925 		tagValue := f.Tag.Get(d.config.TagName)
   902 		keyName := f.Name
   926 		keyName := f.Name
   903 
   927 
       
   928 		if tagValue == "" && d.config.IgnoreUntaggedFields {
       
   929 			continue
       
   930 		}
       
   931 
   904 		// If Squash is set in the config, we squash the field down.
   932 		// If Squash is set in the config, we squash the field down.
   905 		squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
   933 		squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
       
   934 
       
   935 		v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
   906 
   936 
   907 		// Determine the name of the key in the map
   937 		// Determine the name of the key in the map
   908 		if index := strings.Index(tagValue, ","); index != -1 {
   938 		if index := strings.Index(tagValue, ","); index != -1 {
   909 			if tagValue[:index] == "-" {
   939 			if tagValue[:index] == "-" {
   910 				continue
   940 				continue
   913 			if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
   943 			if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
   914 				continue
   944 				continue
   915 			}
   945 			}
   916 
   946 
   917 			// If "squash" is specified in the tag, we squash the field down.
   947 			// If "squash" is specified in the tag, we squash the field down.
   918 			squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1
   948 			squash = squash || strings.Index(tagValue[index+1:], "squash") != -1
   919 			if squash {
   949 			if squash {
   920 				// When squashing, the embedded type can be a pointer to a struct.
   950 				// When squashing, the embedded type can be a pointer to a struct.
   921 				if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
   951 				if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
   922 					v = v.Elem()
   952 					v = v.Elem()
   923 				}
   953 				}
   925 				// The final type must be a struct
   955 				// The final type must be a struct
   926 				if v.Kind() != reflect.Struct {
   956 				if v.Kind() != reflect.Struct {
   927 					return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
   957 					return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
   928 				}
   958 				}
   929 			}
   959 			}
   930 			keyName = tagValue[:index]
   960 			if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
       
   961 				keyName = keyNameTagValue
       
   962 			}
   931 		} else if len(tagValue) > 0 {
   963 		} else if len(tagValue) > 0 {
   932 			if tagValue == "-" {
   964 			if tagValue == "-" {
   933 				continue
   965 				continue
   934 			}
   966 			}
   935 			keyName = tagValue
   967 			keyName = tagValue
  1081 		return fmt.Errorf(
  1113 		return fmt.Errorf(
  1082 			"'%s': source data must be an array or slice, got %s", name, dataValKind)
  1114 			"'%s': source data must be an array or slice, got %s", name, dataValKind)
  1083 	}
  1115 	}
  1084 
  1116 
  1085 	// If the input value is nil, then don't allocate since empty != nil
  1117 	// If the input value is nil, then don't allocate since empty != nil
  1086 	if dataVal.IsNil() {
  1118 	if dataValKind != reflect.Array && dataVal.IsNil() {
  1087 		return nil
  1119 		return nil
  1088 	}
  1120 	}
  1089 
  1121 
  1090 	valSlice := val
  1122 	valSlice := val
  1091 	if valSlice.IsNil() || d.config.ZeroFields {
  1123 	if valSlice.IsNil() || d.config.ZeroFields {
  1243 	for _, dataValKey := range dataVal.MapKeys() {
  1275 	for _, dataValKey := range dataVal.MapKeys() {
  1244 		dataValKeys[dataValKey] = struct{}{}
  1276 		dataValKeys[dataValKey] = struct{}{}
  1245 		dataValKeysUnused[dataValKey.Interface()] = struct{}{}
  1277 		dataValKeysUnused[dataValKey.Interface()] = struct{}{}
  1246 	}
  1278 	}
  1247 
  1279 
       
  1280 	targetValKeysUnused := make(map[interface{}]struct{})
  1248 	errors := make([]string, 0)
  1281 	errors := make([]string, 0)
  1249 
  1282 
  1250 	// This slice will keep track of all the structs we'll be decoding.
  1283 	// This slice will keep track of all the structs we'll be decoding.
  1251 	// There can be more than one struct if there are embedded structs
  1284 	// There can be more than one struct if there are embedded structs
  1252 	// that are squashed.
  1285 	// that are squashed.
  1338 				if !ok {
  1371 				if !ok {
  1339 					// Not a string key
  1372 					// Not a string key
  1340 					continue
  1373 					continue
  1341 				}
  1374 				}
  1342 
  1375 
  1343 				if strings.EqualFold(mK, fieldName) {
  1376 				if d.config.MatchName(mK, fieldName) {
  1344 					rawMapKey = dataValKey
  1377 					rawMapKey = dataValKey
  1345 					rawMapVal = dataVal.MapIndex(dataValKey)
  1378 					rawMapVal = dataVal.MapIndex(dataValKey)
  1346 					break
  1379 					break
  1347 				}
  1380 				}
  1348 			}
  1381 			}
  1349 
  1382 
  1350 			if !rawMapVal.IsValid() {
  1383 			if !rawMapVal.IsValid() {
  1351 				// There was no matching key in the map for the value in
  1384 				// There was no matching key in the map for the value in
  1352 				// the struct. Just ignore.
  1385 				// the struct. Remember it for potential errors and metadata.
       
  1386 				targetValKeysUnused[fieldName] = struct{}{}
  1353 				continue
  1387 				continue
  1354 			}
  1388 			}
  1355 		}
  1389 		}
  1356 
  1390 
  1357 		if !fieldValue.IsValid() {
  1391 		if !fieldValue.IsValid() {
  1407 
  1441 
  1408 		err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
  1442 		err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
  1409 		errors = appendErrors(errors, err)
  1443 		errors = appendErrors(errors, err)
  1410 	}
  1444 	}
  1411 
  1445 
       
  1446 	if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
       
  1447 		keys := make([]string, 0, len(targetValKeysUnused))
       
  1448 		for rawKey := range targetValKeysUnused {
       
  1449 			keys = append(keys, rawKey.(string))
       
  1450 		}
       
  1451 		sort.Strings(keys)
       
  1452 
       
  1453 		err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
       
  1454 		errors = appendErrors(errors, err)
       
  1455 	}
       
  1456 
  1412 	if len(errors) > 0 {
  1457 	if len(errors) > 0 {
  1413 		return &Error{errors}
  1458 		return &Error{errors}
  1414 	}
  1459 	}
  1415 
  1460 
  1416 	// Add the unused keys to the list of unused keys if we're tracking metadata
  1461 	// Add the unused keys to the list of unused keys if we're tracking metadata
  1420 			if name != "" {
  1465 			if name != "" {
  1421 				key = name + "." + key
  1466 				key = name + "." + key
  1422 			}
  1467 			}
  1423 
  1468 
  1424 			d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
  1469 			d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
       
  1470 		}
       
  1471 		for rawKey := range targetValKeysUnused {
       
  1472 			key := rawKey.(string)
       
  1473 			if name != "" {
       
  1474 				key = name + "." + key
       
  1475 			}
       
  1476 
       
  1477 			d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)
  1425 		}
  1478 		}
  1426 	}
  1479 	}
  1427 
  1480 
  1428 	return nil
  1481 	return nil
  1429 }
  1482 }
  1458 		return reflect.Float32
  1511 		return reflect.Float32
  1459 	default:
  1512 	default:
  1460 		return kind
  1513 		return kind
  1461 	}
  1514 	}
  1462 }
  1515 }
       
  1516 
       
  1517 func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {
       
  1518 	for i := 0; i < typ.NumField(); i++ {
       
  1519 		f := typ.Field(i)
       
  1520 		if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
       
  1521 			return true
       
  1522 		}
       
  1523 		if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
       
  1524 			return true
       
  1525 		}
       
  1526 	}
       
  1527 	return false
       
  1528 }
       
  1529 
       
  1530 func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
       
  1531 	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
       
  1532 		return v
       
  1533 	}
       
  1534 	deref := v.Elem()
       
  1535 	derefT := deref.Type()
       
  1536 	if isStructTypeConvertibleToMap(derefT, true, tagName) {
       
  1537 		return deref
       
  1538 	}
       
  1539 	return v
       
  1540 }