| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- // 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
- }
- // 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
- }
- // 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
- }
- // MarshalJSONTo implements [jsonv2.MarshalerTo].
- func (o Value[T]) MarshalJSONTo(enc *jsontext.Encoder) error {
- if !o.set {
- return enc.WriteToken(jsontext.Null)
- }
- return jsonv2.MarshalEncode(enc, &o.value)
- }
- // UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
- func (o *Value[T]) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
- if dec.PeekKind() == 'n' {
- *o = Value[T]{}
- _, err := dec.ReadToken() // read null
- return err
- }
- o.set = true
- return jsonv2.UnmarshalDecode(dec, &o.value)
- }
- // MarshalJSON implements [json.Marshaler].
- func (o Value[T]) MarshalJSON() ([]byte, error) {
- return jsonv2.Marshal(o) // uses MarshalJSONTo
- }
- // UnmarshalJSON implements [json.Unmarshaler].
- func (o *Value[T]) UnmarshalJSON(b []byte) error {
- return jsonv2.Unmarshal(b, o) // uses UnmarshalJSONFrom
- }
|