-
Notifications
You must be signed in to change notification settings - Fork 4
/
prepare.go
97 lines (90 loc) · 2.51 KB
/
prepare.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
package logfmt
import (
"fmt"
"reflect"
)
type LogStringer interface {
LogString() string
}
type valuePrepareFn func(interface{}) (string, reflect.Kind, bool)
var stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
var errorType = reflect.TypeOf((*error)(nil)).Elem()
var logStringerType = reflect.TypeOf((*LogStringer)(nil)).Elem()
var strFuncType = reflect.TypeOf((func() string)(nil))
// WARNING! Sequence of types must match in both findValuePrepareFn() and prepareValue()
// this is a bit slower vs array scan, but may help the compiler with escape analysis
func findPrepareValueFn(t reflect.Type) valuePrepareFn {
switch {
case t.AssignableTo(logStringerType):
return func(value interface{}) (string, reflect.Kind, bool) {
switch vv := value.(LogStringer); {
case vv != nil:
v := vv.LogString()
return v, reflect.Interface, false
default:
return "", reflect.Interface, true
}
}
case t.AssignableTo(strFuncType):
return func(value interface{}) (string, reflect.Kind, bool) {
switch vv := value.(func() string); {
case vv != nil:
v := vv()
return v, reflect.Func, false
default:
return "", reflect.Func, true
}
}
case t.AssignableTo(errorType):
return func(value interface{}) (string, reflect.Kind, bool) {
switch vv := value.(error); {
case vv != nil:
v := vv.Error()
return v, reflect.Interface, false
default:
return "", reflect.Interface, true
}
}
case t.AssignableTo(stringerType):
return func(value interface{}) (string, reflect.Kind, bool) {
switch vv := value.(fmt.Stringer); {
case vv != nil:
v := vv.String()
return v, reflect.Interface, false
default:
return "", reflect.Interface, true
}
}
default:
return nil
}
}
// WARNING! Sequence of types must match in both findValuePrepareFn() and prepareValue()
func prepareValue(iv interface{}) (string, reflect.Kind, bool) {
switch vv := iv.(type) {
case nil:
return "", reflect.Interface, true
case LogStringer:
if !isUnnamedStruct(iv) {
return vv.LogString(), reflect.Interface, false
}
case func() string:
if vv == nil {
return "", reflect.Func, true
}
return vv(), reflect.Func, false
case error:
if !isUnnamedStruct(iv) {
return vv.Error(), reflect.Interface, false
}
case fmt.Stringer:
if !isUnnamedStruct(iv) {
return vv.String(), reflect.Interface, false
}
}
return "", reflect.Invalid, false
}
func isUnnamedStruct(iv interface{}) bool {
t := reflect.TypeOf(iv)
return t.Kind() == reflect.Struct && t.PkgPath() == ""
}