]> Cypherpunks.ru repositories - gostls13.git/blob - src/log/slog/value.go
log/slog: initial commit
[gostls13.git] / src / log / slog / value.go
1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package slog
6
7 import (
8         "fmt"
9         "math"
10         "slices"
11         "strconv"
12         "time"
13 )
14
15 // Definitions for Value.
16 // The Value type itself can be found in value_{safe,unsafe}.go.
17
18 // Kind is the kind of a Value.
19 type Kind int
20
21 // The following list is sorted alphabetically, but it's also important that
22 // KindAny is 0 so that a zero Value represents nil.
23
24 const (
25         KindAny Kind = iota
26         KindBool
27         KindDuration
28         KindFloat64
29         KindInt64
30         KindString
31         KindTime
32         KindUint64
33         KindGroup
34         KindLogValuer
35 )
36
37 var kindStrings = []string{
38         "Any",
39         "Bool",
40         "Duration",
41         "Float64",
42         "Int64",
43         "String",
44         "Time",
45         "Uint64",
46         "Group",
47         "LogValuer",
48 }
49
50 func (k Kind) String() string {
51         if k >= 0 && int(k) < len(kindStrings) {
52                 return kindStrings[k]
53         }
54         return "<unknown slog.Kind>"
55 }
56
57 // Unexported version of Kind, just so we can store Kinds in Values.
58 // (No user-provided value has this type.)
59 type kind Kind
60
61 //////////////// Constructors
62
63 // IntValue returns a Value for an int.
64 func IntValue(v int) Value {
65         return Int64Value(int64(v))
66 }
67
68 // Int64Value returns a Value for an int64.
69 func Int64Value(v int64) Value {
70         return Value{num: uint64(v), any: KindInt64}
71 }
72
73 // Uint64Value returns a Value for a uint64.
74 func Uint64Value(v uint64) Value {
75         return Value{num: v, any: KindUint64}
76 }
77
78 // Float64Value returns a Value for a floating-point number.
79 func Float64Value(v float64) Value {
80         return Value{num: math.Float64bits(v), any: KindFloat64}
81 }
82
83 // BoolValue returns a Value for a bool.
84 func BoolValue(v bool) Value {
85         u := uint64(0)
86         if v {
87                 u = 1
88         }
89         return Value{num: u, any: KindBool}
90 }
91
92 // Unexported version of *time.Location, just so we can store *time.Locations in
93 // Values. (No user-provided value has this type.)
94 type timeLocation *time.Location
95
96 // TimeValue returns a Value for a time.Time.
97 // It discards the monotonic portion.
98 func TimeValue(v time.Time) Value {
99         if v.IsZero() {
100                 // UnixNano on the zero time is undefined, so represent the zero time
101                 // with a nil *time.Location instead. time.Time.Location method never
102                 // returns nil, so a Value with any == timeLocation(nil) cannot be
103                 // mistaken for any other Value, time.Time or otherwise.
104                 return Value{any: timeLocation(nil)}
105         }
106         return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
107 }
108
109 // DurationValue returns a Value for a time.Duration.
110 func DurationValue(v time.Duration) Value {
111         return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
112 }
113
114 // GroupValue returns a new Value for a list of Attrs.
115 // The caller must not subsequently mutate the argument slice.
116 func GroupValue(as ...Attr) Value {
117         return groupValue(as)
118 }
119
120 // AnyValue returns a Value for the supplied value.
121 //
122 // If the supplied value is of type Value, it is returned
123 // unmodified.
124 //
125 // Given a value of one of Go's predeclared string, bool, or
126 // (non-complex) numeric types, AnyValue returns a Value of kind
127 // String, Bool, Uint64, Int64, or Float64. The width of the
128 // original numeric type is not preserved.
129 //
130 // Given a time.Time or time.Duration value, AnyValue returns a Value of kind
131 // KindTime or KindDuration. The monotonic time is not preserved.
132 //
133 // For nil, or values of all other types, including named types whose
134 // underlying type is numeric, AnyValue returns a value of kind KindAny.
135 func AnyValue(v any) Value {
136         switch v := v.(type) {
137         case string:
138                 return StringValue(v)
139         case int:
140                 return Int64Value(int64(v))
141         case uint:
142                 return Uint64Value(uint64(v))
143         case int64:
144                 return Int64Value(v)
145         case uint64:
146                 return Uint64Value(v)
147         case bool:
148                 return BoolValue(v)
149         case time.Duration:
150                 return DurationValue(v)
151         case time.Time:
152                 return TimeValue(v)
153         case uint8:
154                 return Uint64Value(uint64(v))
155         case uint16:
156                 return Uint64Value(uint64(v))
157         case uint32:
158                 return Uint64Value(uint64(v))
159         case uintptr:
160                 return Uint64Value(uint64(v))
161         case int8:
162                 return Int64Value(int64(v))
163         case int16:
164                 return Int64Value(int64(v))
165         case int32:
166                 return Int64Value(int64(v))
167         case float64:
168                 return Float64Value(v)
169         case float32:
170                 return Float64Value(float64(v))
171         case []Attr:
172                 return GroupValue(v...)
173         case Kind:
174                 return Value{any: kind(v)}
175         case Value:
176                 return v
177         default:
178                 return Value{any: v}
179         }
180 }
181
182 //////////////// Accessors
183
184 // Any returns v's value as an any.
185 func (v Value) Any() any {
186         switch v.Kind() {
187         case KindAny:
188                 if k, ok := v.any.(kind); ok {
189                         return Kind(k)
190                 }
191                 return v.any
192         case KindLogValuer:
193                 return v.any
194         case KindGroup:
195                 return v.uncheckedGroup()
196         case KindInt64:
197                 return int64(v.num)
198         case KindUint64:
199                 return v.num
200         case KindFloat64:
201                 return v.float()
202         case KindString:
203                 return v.str()
204         case KindBool:
205                 return v.bool()
206         case KindDuration:
207                 return v.duration()
208         case KindTime:
209                 return v.time()
210         default:
211                 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
212         }
213 }
214
215 // Int64 returns v's value as an int64. It panics
216 // if v is not a signed integer.
217 func (v Value) Int64() int64 {
218         if g, w := v.Kind(), KindInt64; g != w {
219                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
220         }
221         return int64(v.num)
222 }
223
224 // Uint64 returns v's value as a uint64. It panics
225 // if v is not an unsigned integer.
226 func (v Value) Uint64() uint64 {
227         if g, w := v.Kind(), KindUint64; g != w {
228                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
229         }
230         return v.num
231 }
232
233 // Bool returns v's value as a bool. It panics
234 // if v is not a bool.
235 func (v Value) Bool() bool {
236         if g, w := v.Kind(), KindBool; g != w {
237                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
238         }
239         return v.bool()
240 }
241
242 func (a Value) bool() bool {
243         return a.num == 1
244 }
245
246 // Duration returns v's value as a time.Duration. It panics
247 // if v is not a time.Duration.
248 func (a Value) Duration() time.Duration {
249         if g, w := a.Kind(), KindDuration; g != w {
250                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
251         }
252
253         return a.duration()
254 }
255
256 func (a Value) duration() time.Duration {
257         return time.Duration(int64(a.num))
258 }
259
260 // Float64 returns v's value as a float64. It panics
261 // if v is not a float64.
262 func (v Value) Float64() float64 {
263         if g, w := v.Kind(), KindFloat64; g != w {
264                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
265         }
266
267         return v.float()
268 }
269
270 func (a Value) float() float64 {
271         return math.Float64frombits(a.num)
272 }
273
274 // Time returns v's value as a time.Time. It panics
275 // if v is not a time.Time.
276 func (v Value) Time() time.Time {
277         if g, w := v.Kind(), KindTime; g != w {
278                 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
279         }
280         return v.time()
281 }
282
283 func (v Value) time() time.Time {
284         loc := v.any.(timeLocation)
285         if loc == nil {
286                 return time.Time{}
287         }
288         return time.Unix(0, int64(v.num)).In(loc)
289 }
290
291 // LogValuer returns v's value as a LogValuer. It panics
292 // if v is not a LogValuer.
293 func (v Value) LogValuer() LogValuer {
294         return v.any.(LogValuer)
295 }
296
297 // Group returns v's value as a []Attr.
298 // It panics if v's Kind is not KindGroup.
299 func (v Value) Group() []Attr {
300         return v.group()
301 }
302
303 //////////////// Other
304
305 // Equal reports whether v and w have equal keys and values.
306 func (v Value) Equal(w Value) bool {
307         k1 := v.Kind()
308         k2 := w.Kind()
309         if k1 != k2 {
310                 return false
311         }
312         switch k1 {
313         case KindInt64, KindUint64, KindBool, KindDuration:
314                 return v.num == w.num
315         case KindString:
316                 return v.str() == w.str()
317         case KindFloat64:
318                 return v.float() == w.float()
319         case KindTime:
320                 return v.time().Equal(w.time())
321         case KindAny, KindLogValuer:
322                 return v.any == w.any // may panic if non-comparable
323         case KindGroup:
324                 return slices.EqualFunc(v.uncheckedGroup(), w.uncheckedGroup(), Attr.Equal)
325         default:
326                 panic(fmt.Sprintf("bad kind: %s", k1))
327         }
328 }
329
330 // append appends a text representation of v to dst.
331 // v is formatted as with fmt.Sprint.
332 func (v Value) append(dst []byte) []byte {
333         switch v.Kind() {
334         case KindString:
335                 return append(dst, v.str()...)
336         case KindInt64:
337                 return strconv.AppendInt(dst, int64(v.num), 10)
338         case KindUint64:
339                 return strconv.AppendUint(dst, v.num, 10)
340         case KindFloat64:
341                 return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
342         case KindBool:
343                 return strconv.AppendBool(dst, v.bool())
344         case KindDuration:
345                 return append(dst, v.duration().String()...)
346         case KindTime:
347                 return append(dst, v.time().String()...)
348         case KindAny, KindGroup, KindLogValuer:
349                 return append(dst, fmt.Sprint(v.any)...)
350         default:
351                 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
352         }
353 }
354
355 // A LogValuer is any Go value that can convert itself into a Value for logging.
356 //
357 // This mechanism may be used to defer expensive operations until they are
358 // needed, or to expand a single value into a sequence of components.
359 type LogValuer interface {
360         LogValue() Value
361 }
362
363 const maxLogValues = 100
364
365 // Resolve repeatedly calls LogValue on v while it implements LogValuer,
366 // and returns the result.
367 // If v resolves to a group, the group's attributes' values are also resolved.
368 // If the number of LogValue calls exceeds a threshold, a Value containing an
369 // error is returned.
370 // Resolve's return value is guaranteed not to be of Kind KindLogValuer.
371 func (v Value) Resolve() Value {
372         v = v.resolve()
373         if v.Kind() == KindGroup {
374                 resolveAttrs(v.Group())
375         }
376         return v
377 }
378
379 func (v Value) resolve() Value {
380         orig := v
381         for i := 0; i < maxLogValues; i++ {
382                 if v.Kind() != KindLogValuer {
383                         return v
384                 }
385                 v = v.LogValuer().LogValue()
386         }
387         err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
388         return AnyValue(err)
389 }
390
391 // resolveAttrs replaces the values of the given Attrs with their
392 // resolutions.
393 func resolveAttrs(as []Attr) {
394         for i, a := range as {
395                 as[i].Value = a.Value.Resolve()
396         }
397 }