Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136// Copyright (c) Tailscale Inc & contributors// 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) } // Pin v2 semantics so the wire format (e.g. nil slices as "[]", // nil maps as "{}") stays the same even when this method is // reached via encoding/json v1, which as of Go 1.27 dispatches // here with v1 options that would otherwise leak into this call. return jsonv2.MarshalEncode(enc, &o.value, jsonv2.DefaultOptionsV2())}
// 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 // Pin v2 semantics; see MarshalJSONTo. return jsonv2.UnmarshalDecode(dec, &o.value, jsonv2.DefaultOptionsV2())}
// 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}