-
Notifications
You must be signed in to change notification settings - Fork 61
/
unmarshal.go
127 lines (102 loc) · 2.92 KB
/
unmarshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
package config
import (
"bytes"
"encoding"
"fmt"
"reflect"
"strconv"
)
func unmarshalValue(dest reflect.Value, keyword string, value []byte) error {
if tu := asTextUnmarshaler(dest); tu != nil {
return tu.UnmarshalText(value)
}
if len(value) == 0 {
dest.Set(reflect.Zero(dest.Type()))
return nil
}
switch dest.Kind() {
case reflect.Bool:
return unmarshalBool(dest, keyword, value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return unmarshalInt(dest, keyword, value)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return unmarshalUint(dest, keyword, value)
case reflect.Float32, reflect.Float64:
return unmarshalFloat(dest, keyword, value)
case reflect.String:
dest.SetString(string(value))
default:
return cannotConvertToType(value, dest.Type())
}
return nil
}
// asTextUnmarshaler checks whether v implements the TextUnmarshaler interface.
func asTextUnmarshaler(v reflect.Value) encoding.TextUnmarshaler {
// Get a pointer to v so that methods with pointer receivers will be included below.
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
if v.Type().NumMethod() > 0 {
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
return u
}
}
return nil
}
func unmarshalBool(dest reflect.Value, keyword string, value []byte) error {
var x bool
var err error
switch s := string(bytes.ToLower(value)); s {
case "y", "yes", "on":
x = true
case "n", "no", "off":
x = false
default:
x, err = strconv.ParseBool(s)
}
if err != nil {
return fmt.Errorf("config: %s expects a boolean value", keyword)
}
dest.SetBool(x)
return nil
}
func unmarshalInt(dest reflect.Value, keyword string, value []byte) error {
x, err := strconv.ParseInt(string(value), 0, 64)
if err != nil {
return fmt.Errorf("config: %s expects an integer value", keyword)
}
if dest.OverflowInt(x) {
return cannotConvertToType(value, dest.Type())
}
dest.SetInt(x)
return nil
}
func unmarshalUint(dest reflect.Value, keyword string, value []byte) error {
x, err := strconv.ParseUint(string(value), 0, 64)
if err != nil {
return fmt.Errorf("config: %s expects a non-negative integer value", keyword)
}
if dest.OverflowUint(x) {
return cannotConvertToType(value, dest.Type())
}
dest.SetUint(x)
return nil
}
func unmarshalFloat(dest reflect.Value, keyword string, value []byte) error {
x, err := strconv.ParseFloat(string(value), dest.Type().Bits())
if err != nil {
return fmt.Errorf("config: %s expects a numeric value", keyword)
}
if dest.OverflowFloat(x) {
return cannotConvertToType(value, dest.Type())
}
dest.SetFloat(x)
return nil
}
func cannotConvertToType(value []byte, t reflect.Type) error {
return fmt.Errorf("config: cannot convert %.50q to type %v", value, t)
}