2024-07-08 15:32:37 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
package opt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
jsonv2 "github.com/go-json-experiment/json"
|
|
|
|
"github.com/go-json-experiment/json/jsontext"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Value is an optional value to be JSON-encoded.
|
|
|
|
// With [encoding/json], a zero Value is marshaled as a JSON null.
|
|
|
|
// With [github.com/go-json-experiment/json], a zero Value is omitted from the
|
|
|
|
// JSON object if the Go struct field specified with omitzero.
|
|
|
|
// The omitempty tag option should never be used with Value fields.
|
|
|
|
type Value[T any] struct {
|
|
|
|
value T
|
|
|
|
set bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Equal reports whether the receiver and the other value are equal.
|
|
|
|
// If the template type T in Value[T] implements an Equal method, it will be used
|
|
|
|
// instead of the == operator for comparing values.
|
|
|
|
type equatable[T any] interface {
|
|
|
|
// Equal reports whether the receiver and the other values are equal.
|
|
|
|
Equal(other T) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValueOf returns an optional Value containing the specified value.
|
|
|
|
// It treats nil slices and maps as empty slices and maps.
|
|
|
|
func ValueOf[T any](v T) Value[T] {
|
|
|
|
return Value[T]{value: v, set: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements [fmt.Stringer].
|
|
|
|
func (o *Value[T]) String() string {
|
|
|
|
if !o.set {
|
|
|
|
return fmt.Sprintf("(empty[%T])", o.value)
|
|
|
|
}
|
|
|
|
return fmt.Sprint(o.value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set assigns the specified value to the optional value o.
|
|
|
|
func (o *Value[T]) Set(v T) {
|
|
|
|
*o = ValueOf(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear resets o to an empty state.
|
|
|
|
func (o *Value[T]) Clear() {
|
|
|
|
*o = Value[T]{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsSet reports whether o has a value set.
|
|
|
|
func (o *Value[T]) IsSet() bool {
|
|
|
|
return o.set
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the value of o.
|
|
|
|
// If a value hasn't been set, a zero value of T will be returned.
|
|
|
|
func (o Value[T]) Get() T {
|
|
|
|
return o.value
|
|
|
|
}
|
|
|
|
|
2024-07-19 20:14:28 +00:00
|
|
|
// GetOr returns the value of o or def if a value hasn't been set.
|
|
|
|
func (o Value[T]) GetOr(def T) T {
|
|
|
|
if o.set {
|
|
|
|
return o.value
|
|
|
|
}
|
|
|
|
return def
|
|
|
|
}
|
|
|
|
|
2024-07-08 15:32:37 +00:00
|
|
|
// Get returns the value and a flag indicating whether the value is set.
|
|
|
|
func (o Value[T]) GetOk() (v T, ok bool) {
|
|
|
|
return o.value, o.set
|
|
|
|
}
|
|
|
|
|
|
|
|
// Equal reports whether o is equal to v.
|
|
|
|
// Two optional values are equal if both are empty,
|
|
|
|
// or if both are set and the underlying values are equal.
|
|
|
|
// If the template type T implements an Equal(T) bool method, it will be used
|
|
|
|
// instead of the == operator for value comparison.
|
|
|
|
// If T is not comparable, it returns false.
|
|
|
|
func (o Value[T]) Equal(v Value[T]) bool {
|
|
|
|
if o.set != v.set {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !o.set {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
ov := any(o.value)
|
|
|
|
if eq, ok := ov.(equatable[T]); ok {
|
|
|
|
return eq.Equal(v.value)
|
|
|
|
}
|
|
|
|
if reflect.TypeFor[T]().Comparable() {
|
|
|
|
return ov == any(v.value)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSONV2 implements [jsonv2.MarshalerV2].
|
|
|
|
func (o Value[T]) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error {
|
|
|
|
if !o.set {
|
|
|
|
return enc.WriteToken(jsontext.Null)
|
|
|
|
}
|
|
|
|
return jsonv2.MarshalEncode(enc, &o.value, opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSONV2 implements [jsonv2.UnmarshalerV2].
|
|
|
|
func (o *Value[T]) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error {
|
|
|
|
if dec.PeekKind() == 'n' {
|
|
|
|
*o = Value[T]{}
|
|
|
|
_, err := dec.ReadToken() // read null
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
o.set = true
|
|
|
|
return jsonv2.UnmarshalDecode(dec, &o.value, opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON implements [json.Marshaler].
|
|
|
|
func (o Value[T]) MarshalJSON() ([]byte, error) {
|
|
|
|
return jsonv2.Marshal(o) // uses MarshalJSONV2
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements [json.Unmarshaler].
|
|
|
|
func (o *Value[T]) UnmarshalJSON(b []byte) error {
|
|
|
|
return jsonv2.Unmarshal(b, o) // uses UnmarshalJSONV2
|
|
|
|
}
|