// 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.

//go:build safe_values

package slog

// This file defines the most portable representation of Value.

// 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 {
	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
	// and nanoseconds since the epoch for TimeKind.
	num uint64
	// s holds the value for StringKind.
	s string
	// If any is of type Kind, then the value is in num or s 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).
	// Otherwise, the Kind is Any and any is the value.
	// (This implies that Values cannot store Kinds or *time.Locations.)
	any any
}

// Kind returns the Value's Kind.
func (v Value) Kind() Kind {
	switch k := v.any.(type) {
	case Kind:
		return k
	case timeLocation:
		return TimeKind
	case []Attr:
		return GroupKind
	case LogValuer:
		return LogValuerKind
	case kind: // a kind is just a wrapper for a Kind
		return AnyKind
	default:
		return AnyKind
	}
}

func (v Value) str() string {
	return v.s
}

// String returns a new Value for a string.
func StringValue(value string) Value {
	return Value{s: value, any: StringKind}
}

// String returns Value's value as a string, formatted like fmt.Sprint. Unlike
// the methods Int64, Float64, and so on, which panic if the Value is of the
// wrong kind, String never panics.
func (v Value) String() string {
	if v.Kind() == StringKind {
		return v.str()
	}
	var buf []byte
	return string(v.append(buf))
}

func groupValue(as []Attr) Value {
	return Value{any: as}
}

func (v Value) group() []Attr {
	return v.any.([]Attr)
}

func (v Value) uncheckedGroup() []Attr { return v.group() }
