/
value.go
159 lines (132 loc) · 3.92 KB
/
value.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package dapper
import (
"fmt"
"reflect"
"sync/atomic"
"github.com/dogmatiq/dapper/internal/unsafereflect"
)
// Value contains information about a Go value that is to be formatted.
type Value struct {
// Value is the value to be formatted.
Value reflect.Value
// DynamicType is the value's type.
DynamicType reflect.Type
// StaticType is the type of the "variable" that the value is stored in,
// which may not be the same as its dynamic type.
//
// For example, when formatting the values within a slice of "any"
// containing integers, such as []any{1, 2, 3}, the DynamicType will be
// "int", but the static type will be "any".
StaticType reflect.Type
// IsAmbiguousDynamicType is true if the value's dynamic type is not clear from
// the context of what has already been rendered.
IsAmbiguousDynamicType bool
// IsAmbiguousStaticType is true if the value's static type is not clear from
// the context of what has already been rendered.
IsAmbiguousStaticType bool
// IsUnexported is true if this value was obtained from an unexported struct
// field. If so, it is not possible to extract the underlying value.
IsUnexported bool
}
// IsAnonymousType returns true if the value has an anonymous type.
func (v *Value) IsAnonymousType() bool {
return v.DynamicType.Name() == ""
}
// IsAmbiguousType returns true if either the dynamic type or the static type is
// ambiguous.
func (v *Value) IsAmbiguousType() bool {
return v.IsAmbiguousDynamicType || v.IsAmbiguousStaticType
}
// Is returns true if v's type is exactly T.
func Is[T any](v Value) bool {
t := typeOf[T]()
if t.Kind() == reflect.Interface {
panic(fmt.Sprintf("%s is an interface", t))
}
if v.DynamicType == t {
return true
}
return false
}
// AsConcrete returns a v as type T if its dynamic type is exactly T.
func AsConcrete[T any](v Value) (T, bool) {
if Is[T](v) {
return v.Value.Interface().(T), true
}
return zero[T](), false
}
// AsImplementationOf returns v as a value of type T if it directly implements
// T.
func AsImplementationOf[T any](v Value) (T, bool) {
t := typeOf[T]()
if t.Kind() != reflect.Interface {
panic(fmt.Sprintf("%s is not an interface", t))
}
// If v is itself an interface it cannot implement anything.
if v.DynamicType.Kind() == reflect.Interface {
return zero[T](), false
}
// If the type is a pointer and the underlying type does not require pointer
// receivers to implement the type we report that the pointer does NOT
// implement the interface, forcing the renderer to descend into the
// underlying type instead.
if v.DynamicType.Kind() == reflect.Ptr {
if v.DynamicType.Elem().Implements(t) {
return zero[T](), false
}
}
if v.DynamicType.Implements(t) {
return v.Value.Interface().(T), true
}
return zero[T](), false
}
// typeOf returns the [reflect.Type] for T.
func typeOf[T any]() reflect.Type {
return reflect.TypeOf((*T)(nil)).Elem()
}
// zero returns the zero value of T.
func zero[T any]() (_ T) {
return
}
// asInt returns the value of v as an int64, if it is one of the signed integer
// types, including atomic types.
func asInt(v reflect.Value) (n int64, ok bool) {
switch v.Kind() {
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
return v.Int(), true
}
v = unsafereflect.MakeMutable(v)
switch v := v.Interface().(type) {
case atomic.Int32:
return int64(v.Load()), true
case atomic.Int64:
return v.Load(), true
default:
return 0, false
}
}
// asUint returns the value of v as a uint64, if it is one of the unsigned
// integer types, including atomic types.
func asUint(v reflect.Value) (n uint64, ok bool) {
switch v.Kind() {
case reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64:
return v.Uint(), true
}
v = unsafereflect.MakeMutable(v)
switch v := v.Interface().(type) {
case atomic.Uint32:
return uint64(v.Load()), true
case atomic.Uint64:
return v.Load(), true
default:
return 0, false
}
}