-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
builtin.go
111 lines (103 loc) · 2.73 KB
/
builtin.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
package tagflag
import (
"errors"
"fmt"
"net"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"golang.org/x/xerrors"
)
var builtinMarshalers = map[reflect.Type]marshaler{}
// Convenience function to allow adding marshalers using typed functions.
// marshalFunc is of type func(arg string) T or func(arg string) (T, error),
// where T is the type the function can marshal.
func addBuiltinDynamicMarshaler(marshalFunc interface{}, explicitValueRequired bool) {
marshalFuncValue := reflect.ValueOf(marshalFunc)
marshalType := marshalFuncValue.Type().Out(0)
builtinMarshalers[marshalType] = dynamicMarshaler{
marshal: func(marshalValue reflect.Value, arg string) error {
out := marshalFuncValue.Call([]reflect.Value{reflect.ValueOf(arg)})
marshalValue.Set(out[0])
if len(out) > 1 {
i := out[1].Interface()
if i != nil {
return i.(error)
}
}
return nil
},
explicitValueRequired: explicitValueRequired,
}
}
func init() {
// These are some simple builtin types that are nice to be handled without
// wrappers that implement Marshaler. Note that if they return pointer
// types, those must be used in the flag struct, because there's no way to
// know that nothing depends on the address returned.
addBuiltinDynamicMarshaler(func(urlStr string) (*url.URL, error) {
return url.Parse(urlStr)
}, false)
// Empty strings for this type are valid, so we enforce that the value is
// explicit (=), so that the user knows what they're getting into.
addBuiltinDynamicMarshaler(func(s string) (*net.TCPAddr, error) {
if s == "" {
return nil, nil
}
var ret net.TCPAddr
retAlt, err := parseIpPortZone(s)
if err != nil {
return nil, err
}
ret = net.TCPAddr(retAlt)
return &ret, err
}, true)
addBuiltinDynamicMarshaler(func(s string) (time.Duration, error) {
return time.ParseDuration(s)
}, false)
addBuiltinDynamicMarshaler(func(s string) (ip net.IP, err error) {
ip = net.ParseIP(s)
if ip == nil {
err = fmt.Errorf("failed to parse IP")
}
return
}, false)
}
func parseIpAddr(host string) (ret net.IPAddr, err error) {
ss := strings.SplitN(host, "%", 2)
ret.IP = net.ParseIP(ss[0])
if ret.IP == nil && ss[0] != "" {
err = errors.New("error parsing IP")
return
}
if len(ss) >= 2 {
ret.Zone = ss[1]
}
return
}
type ipPortZone struct {
IP net.IP
Port int
Zone string
}
func parseIpPortZone(hostport string) (ret ipPortZone, err error) {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
return
}
portInt64, err := strconv.ParseInt(port, 10, 0)
if err != nil {
return
}
ret.Port = int(portInt64)
ipAddr, err := parseIpAddr(host)
if err != nil {
err = xerrors.Errorf("parsing host %q: %w", host, err)
return
}
ret.IP = ipAddr.IP
ret.Zone = ipAddr.Zone
return
}