forked from rainycape/gondola
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse.go
121 lines (116 loc) · 2.88 KB
/
parse.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
package input
import (
"fmt"
"gnd.la/i18n"
"gnd.la/util/types"
"math"
"reflect"
"strconv"
"strings"
)
var (
parserInterface = reflect.TypeOf((*Parser)(nil)).Elem()
)
// Parser is the interface implemented by types
// that know how to parse themselves from a user
// provided string.
type Parser interface {
Parse(s string) error
}
// Parse tries to parse an string into the given argument.
// e.g.
// var f float32
// Parse("27.5", &f)
// var width uint
// Parse("57", &width)
// Supported types are: string, bool, u?int(8|16|32|64)? and float(32|64). If
// the parsed value would overflow the given type, the maximum value
// (or minimum, if it's negative) for the type will be set.
// If arg implements the Parser interface, its Parse method will
// be used instead.
func Parse(val string, arg interface{}) error {
if parser, ok := arg.(Parser); ok {
return parser.Parse(val)
}
v, err := types.SettableValue(arg)
if err != nil {
return err
}
return parse(val, v)
}
func parse(val string, v reflect.Value) error {
var err error
p := v
// Get Pointer methods
if p.IsValid() && p.Kind() != reflect.Ptr && p.CanAddr() {
p = p.Addr()
}
if p.Type().Implements(parserInterface) {
err = p.Interface().(Parser).Parse(val)
if err == nil {
return nil
}
// Continue with the function, just in case we can
// still parse the value (e.g. an enum type which defines
// a Parse() function for accepting raw strings but val
// is actually a numeric value).
}
// If val is empty, set the value to zero
if val == "" {
v.Set(reflect.Zero(v.Type()))
return nil
}
switch v.Type().Kind() {
case reflect.Bool:
res := false
switch strings.ToLower(val) {
case "", "f", "false", "0", "off":
case "t", "true", "1", "on":
res = true
default:
return i18n.Errorf("invalid boolean value %q", val)
}
v.SetBool(res)
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
res, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return err
}
if v.OverflowInt(res) {
if res > 0 {
res = int64(math.Pow(2, float64(8*v.Type().Size()-1)) - 1)
} else {
res = -int64(math.Pow(2, float64(8*v.Type().Size()-1)))
}
}
v.SetInt(res)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
res, err := strconv.ParseUint(val, 0, 64)
if err != nil {
return err
}
if v.OverflowUint(res) {
res = uint64(math.Pow(2, float64(8*v.Type().Size())) - 1)
}
v.SetUint(res)
return nil
case reflect.Float32, reflect.Float64:
res, err := strconv.ParseFloat(val, 64)
if err != nil {
return err
}
v.SetFloat(res)
return nil
case reflect.String:
v.SetString(val)
return nil
default:
if err == nil {
err = fmt.Errorf("Invalid argument type passed to Parse(): %s. Please, see the documentation for a list of the supported types.",
v.Type())
}
}
return err
}