summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/exp/slog/value.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/exp/slog/value.go')
-rw-r--r--vendor/golang.org/x/exp/slog/value.go456
1 files changed, 456 insertions, 0 deletions
diff --git a/vendor/golang.org/x/exp/slog/value.go b/vendor/golang.org/x/exp/slog/value.go
new file mode 100644
index 000000000..3550c46fc
--- /dev/null
+++ b/vendor/golang.org/x/exp/slog/value.go
@@ -0,0 +1,456 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package slog
+
+import (
+ "fmt"
+ "math"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+ "unsafe"
+
+ "golang.org/x/exp/slices"
+)
+
+// A Value can represent any Go value, but unlike type any,
+// it can represent most small values without an allocation.
+// The zero Value corresponds to nil.
+type Value struct {
+ _ [0]func() // disallow ==
+ // num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
+ // the string length for KindString, and nanoseconds since the epoch for KindTime.
+ num uint64
+ // If any is of type Kind, then the value is in num as described above.
+ // If any is of type *time.Location, then the Kind is Time and time.Time value
+ // can be constructed from the Unix nanos in num and the location (monotonic time
+ // is not preserved).
+ // If any is of type stringptr, then the Kind is String and the string value
+ // consists of the length in num and the pointer in any.
+ // Otherwise, the Kind is Any and any is the value.
+ // (This implies that Attrs cannot store values of type Kind, *time.Location
+ // or stringptr.)
+ any any
+}
+
+// Kind is the kind of a Value.
+type Kind int
+
+// The following list is sorted alphabetically, but it's also important that
+// KindAny is 0 so that a zero Value represents nil.
+
+const (
+ KindAny Kind = iota
+ KindBool
+ KindDuration
+ KindFloat64
+ KindInt64
+ KindString
+ KindTime
+ KindUint64
+ KindGroup
+ KindLogValuer
+)
+
+var kindStrings = []string{
+ "Any",
+ "Bool",
+ "Duration",
+ "Float64",
+ "Int64",
+ "String",
+ "Time",
+ "Uint64",
+ "Group",
+ "LogValuer",
+}
+
+func (k Kind) String() string {
+ if k >= 0 && int(k) < len(kindStrings) {
+ return kindStrings[k]
+ }
+ return "<unknown slog.Kind>"
+}
+
+// Unexported version of Kind, just so we can store Kinds in Values.
+// (No user-provided value has this type.)
+type kind Kind
+
+// Kind returns v's Kind.
+func (v Value) Kind() Kind {
+ switch x := v.any.(type) {
+ case Kind:
+ return x
+ case stringptr:
+ return KindString
+ case timeLocation:
+ return KindTime
+ case groupptr:
+ return KindGroup
+ case LogValuer:
+ return KindLogValuer
+ case kind: // a kind is just a wrapper for a Kind
+ return KindAny
+ default:
+ return KindAny
+ }
+}
+
+//////////////// Constructors
+
+// IntValue returns a Value for an int.
+func IntValue(v int) Value {
+ return Int64Value(int64(v))
+}
+
+// Int64Value returns a Value for an int64.
+func Int64Value(v int64) Value {
+ return Value{num: uint64(v), any: KindInt64}
+}
+
+// Uint64Value returns a Value for a uint64.
+func Uint64Value(v uint64) Value {
+ return Value{num: v, any: KindUint64}
+}
+
+// Float64Value returns a Value for a floating-point number.
+func Float64Value(v float64) Value {
+ return Value{num: math.Float64bits(v), any: KindFloat64}
+}
+
+// BoolValue returns a Value for a bool.
+func BoolValue(v bool) Value {
+ u := uint64(0)
+ if v {
+ u = 1
+ }
+ return Value{num: u, any: KindBool}
+}
+
+// Unexported version of *time.Location, just so we can store *time.Locations in
+// Values. (No user-provided value has this type.)
+type timeLocation *time.Location
+
+// TimeValue returns a Value for a time.Time.
+// It discards the monotonic portion.
+func TimeValue(v time.Time) Value {
+ if v.IsZero() {
+ // UnixNano on the zero time is undefined, so represent the zero time
+ // with a nil *time.Location instead. time.Time.Location method never
+ // returns nil, so a Value with any == timeLocation(nil) cannot be
+ // mistaken for any other Value, time.Time or otherwise.
+ return Value{any: timeLocation(nil)}
+ }
+ return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
+}
+
+// DurationValue returns a Value for a time.Duration.
+func DurationValue(v time.Duration) Value {
+ return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
+}
+
+// AnyValue returns a Value for the supplied value.
+//
+// If the supplied value is of type Value, it is returned
+// unmodified.
+//
+// Given a value of one of Go's predeclared string, bool, or
+// (non-complex) numeric types, AnyValue returns a Value of kind
+// String, Bool, Uint64, Int64, or Float64. The width of the
+// original numeric type is not preserved.
+//
+// Given a time.Time or time.Duration value, AnyValue returns a Value of kind
+// KindTime or KindDuration. The monotonic time is not preserved.
+//
+// For nil, or values of all other types, including named types whose
+// underlying type is numeric, AnyValue returns a value of kind KindAny.
+func AnyValue(v any) Value {
+ switch v := v.(type) {
+ case string:
+ return StringValue(v)
+ case int:
+ return Int64Value(int64(v))
+ case uint:
+ return Uint64Value(uint64(v))
+ case int64:
+ return Int64Value(v)
+ case uint64:
+ return Uint64Value(v)
+ case bool:
+ return BoolValue(v)
+ case time.Duration:
+ return DurationValue(v)
+ case time.Time:
+ return TimeValue(v)
+ case uint8:
+ return Uint64Value(uint64(v))
+ case uint16:
+ return Uint64Value(uint64(v))
+ case uint32:
+ return Uint64Value(uint64(v))
+ case uintptr:
+ return Uint64Value(uint64(v))
+ case int8:
+ return Int64Value(int64(v))
+ case int16:
+ return Int64Value(int64(v))
+ case int32:
+ return Int64Value(int64(v))
+ case float64:
+ return Float64Value(v)
+ case float32:
+ return Float64Value(float64(v))
+ case []Attr:
+ return GroupValue(v...)
+ case Kind:
+ return Value{any: kind(v)}
+ case Value:
+ return v
+ default:
+ return Value{any: v}
+ }
+}
+
+//////////////// Accessors
+
+// Any returns v's value as an any.
+func (v Value) Any() any {
+ switch v.Kind() {
+ case KindAny:
+ if k, ok := v.any.(kind); ok {
+ return Kind(k)
+ }
+ return v.any
+ case KindLogValuer:
+ return v.any
+ case KindGroup:
+ return v.group()
+ case KindInt64:
+ return int64(v.num)
+ case KindUint64:
+ return v.num
+ case KindFloat64:
+ return v.float()
+ case KindString:
+ return v.str()
+ case KindBool:
+ return v.bool()
+ case KindDuration:
+ return v.duration()
+ case KindTime:
+ return v.time()
+ default:
+ panic(fmt.Sprintf("bad kind: %s", v.Kind()))
+ }
+}
+
+// Int64 returns v's value as an int64. It panics
+// if v is not a signed integer.
+func (v Value) Int64() int64 {
+ if g, w := v.Kind(), KindInt64; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+ return int64(v.num)
+}
+
+// Uint64 returns v's value as a uint64. It panics
+// if v is not an unsigned integer.
+func (v Value) Uint64() uint64 {
+ if g, w := v.Kind(), KindUint64; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+ return v.num
+}
+
+// Bool returns v's value as a bool. It panics
+// if v is not a bool.
+func (v Value) Bool() bool {
+ if g, w := v.Kind(), KindBool; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+ return v.bool()
+}
+
+func (v Value) bool() bool {
+ return v.num == 1
+}
+
+// Duration returns v's value as a time.Duration. It panics
+// if v is not a time.Duration.
+func (v Value) Duration() time.Duration {
+ if g, w := v.Kind(), KindDuration; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+
+ return v.duration()
+}
+
+func (v Value) duration() time.Duration {
+ return time.Duration(int64(v.num))
+}
+
+// Float64 returns v's value as a float64. It panics
+// if v is not a float64.
+func (v Value) Float64() float64 {
+ if g, w := v.Kind(), KindFloat64; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+
+ return v.float()
+}
+
+func (v Value) float() float64 {
+ return math.Float64frombits(v.num)
+}
+
+// Time returns v's value as a time.Time. It panics
+// if v is not a time.Time.
+func (v Value) Time() time.Time {
+ if g, w := v.Kind(), KindTime; g != w {
+ panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
+ }
+ return v.time()
+}
+
+func (v Value) time() time.Time {
+ loc := v.any.(timeLocation)
+ if loc == nil {
+ return time.Time{}
+ }
+ return time.Unix(0, int64(v.num)).In(loc)
+}
+
+// LogValuer returns v's value as a LogValuer. It panics
+// if v is not a LogValuer.
+func (v Value) LogValuer() LogValuer {
+ return v.any.(LogValuer)
+}
+
+// Group returns v's value as a []Attr.
+// It panics if v's Kind is not KindGroup.
+func (v Value) Group() []Attr {
+ if sp, ok := v.any.(groupptr); ok {
+ return unsafe.Slice((*Attr)(sp), v.num)
+ }
+ panic("Group: bad kind")
+}
+
+func (v Value) group() []Attr {
+ return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
+}
+
+//////////////// Other
+
+// Equal reports whether v and w represent the same Go value.
+func (v Value) Equal(w Value) bool {
+ k1 := v.Kind()
+ k2 := w.Kind()
+ if k1 != k2 {
+ return false
+ }
+ switch k1 {
+ case KindInt64, KindUint64, KindBool, KindDuration:
+ return v.num == w.num
+ case KindString:
+ return v.str() == w.str()
+ case KindFloat64:
+ return v.float() == w.float()
+ case KindTime:
+ return v.time().Equal(w.time())
+ case KindAny, KindLogValuer:
+ return v.any == w.any // may panic if non-comparable
+ case KindGroup:
+ return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
+ default:
+ panic(fmt.Sprintf("bad kind: %s", k1))
+ }
+}
+
+// append appends a text representation of v to dst.
+// v is formatted as with fmt.Sprint.
+func (v Value) append(dst []byte) []byte {
+ switch v.Kind() {
+ case KindString:
+ return append(dst, v.str()...)
+ case KindInt64:
+ return strconv.AppendInt(dst, int64(v.num), 10)
+ case KindUint64:
+ return strconv.AppendUint(dst, v.num, 10)
+ case KindFloat64:
+ return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
+ case KindBool:
+ return strconv.AppendBool(dst, v.bool())
+ case KindDuration:
+ return append(dst, v.duration().String()...)
+ case KindTime:
+ return append(dst, v.time().String()...)
+ case KindGroup:
+ return fmt.Append(dst, v.group())
+ case KindAny, KindLogValuer:
+ return fmt.Append(dst, v.any)
+ default:
+ panic(fmt.Sprintf("bad kind: %s", v.Kind()))
+ }
+}
+
+// A LogValuer is any Go value that can convert itself into a Value for logging.
+//
+// This mechanism may be used to defer expensive operations until they are
+// needed, or to expand a single value into a sequence of components.
+type LogValuer interface {
+ LogValue() Value
+}
+
+const maxLogValues = 100
+
+// Resolve repeatedly calls LogValue on v while it implements LogValuer,
+// and returns the result.
+// If v resolves to a group, the group's attributes' values are not recursively
+// resolved.
+// If the number of LogValue calls exceeds a threshold, a Value containing an
+// error is returned.
+// Resolve's return value is guaranteed not to be of Kind KindLogValuer.
+func (v Value) Resolve() (rv Value) {
+ orig := v
+ defer func() {
+ if r := recover(); r != nil {
+ rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
+ }
+ }()
+
+ for i := 0; i < maxLogValues; i++ {
+ if v.Kind() != KindLogValuer {
+ return v
+ }
+ v = v.LogValuer().LogValue()
+ }
+ err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
+ return AnyValue(err)
+}
+
+func stack(skip, nFrames int) string {
+ pcs := make([]uintptr, nFrames+1)
+ n := runtime.Callers(skip+1, pcs)
+ if n == 0 {
+ return "(no stack)"
+ }
+ frames := runtime.CallersFrames(pcs[:n])
+ var b strings.Builder
+ i := 0
+ for {
+ frame, more := frames.Next()
+ fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line)
+ if !more {
+ break
+ }
+ i++
+ if i >= nFrames {
+ fmt.Fprintf(&b, "(rest of stack elided)\n")
+ break
+ }
+ }
+ return b.String()
+}