mirror of https://github.com/jetkvm/kvm.git
155 lines
3.3 KiB
Go
155 lines
3.3 KiB
Go
//go:build go1.22
|
|
|
|
package null
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// Value represents a value that may be null.
|
|
type Value[T any] struct {
|
|
sql.Null[T]
|
|
}
|
|
|
|
// NewValue creates a new Value.
|
|
func NewValue[T any](t T, valid bool) Value[T] {
|
|
return Value[T]{
|
|
Null: sql.Null[T]{
|
|
V: t,
|
|
Valid: valid,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ValueFrom creates a new Value that will always be valid.
|
|
func ValueFrom[T any](t T) Value[T] {
|
|
return NewValue(t, true)
|
|
}
|
|
|
|
// ValueFromPtr creates a new Value that will be null if t is nil.
|
|
func ValueFromPtr[T any](t *T) Value[T] {
|
|
if t == nil {
|
|
var zero T
|
|
return NewValue(zero, false)
|
|
}
|
|
return NewValue(*t, true)
|
|
}
|
|
|
|
// ValueOrZero returns the inner value if valid, otherwise zero.
|
|
func (t Value[T]) ValueOrZero() T {
|
|
if !t.Valid {
|
|
var zero T
|
|
return zero
|
|
}
|
|
return t.V
|
|
}
|
|
|
|
// ValueOr returns the inner value if valid, otherwise v.
|
|
func (t Value[T]) ValueOr(v T) T {
|
|
if !t.Valid {
|
|
return v
|
|
}
|
|
return t.V
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.
|
|
// It will encode null if this value is null.
|
|
func (t Value[T]) MarshalJSON() ([]byte, error) {
|
|
if !t.Valid {
|
|
return []byte("null"), nil
|
|
}
|
|
return json.Marshal(t.V)
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.
|
|
// It supports string and null input.
|
|
func (t *Value[T]) UnmarshalJSON(data []byte) error {
|
|
if len(data) > 0 && data[0] == 'n' {
|
|
t.Valid = false
|
|
return nil
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &t.V); err != nil {
|
|
return fmt.Errorf("null: couldn't unmarshal JSON: %w", err)
|
|
}
|
|
|
|
t.Valid = true
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
// MarshalText implements encoding.TextMarshaler.
|
|
// It returns an empty string if invalid, otherwise T's MarshalText.
|
|
func (t Value[T]) MarshalText() ([]byte, error) {
|
|
if !t.Valid {
|
|
return []byte{}, nil
|
|
}
|
|
if tm, ok := any(t.V).(encoding.TextMarshaler); ok {
|
|
return tm.MarshalText()
|
|
}
|
|
|
|
rv := reflect.ValueOf(t.V)
|
|
if !rv.IsValid() {
|
|
return []byte{}, nil
|
|
}
|
|
|
|
try:
|
|
switch rv.Kind() {
|
|
case reflect.Pointer:
|
|
if rv.IsNil() {
|
|
return []byte{}, nil
|
|
}
|
|
rv = rv.Elem()
|
|
goto try
|
|
case reflect.String:
|
|
return []byte(rv.String()), nil
|
|
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
|
|
return []byte(strconv.FormatInt(rv.Int(), 10)), nil
|
|
case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
|
|
return []byte(strconv.FormatUint(rv.Uint(), 10)), nil
|
|
case reflect.Float32, reflect.Float64:
|
|
return []byte(strconv.FormatFloat(rv.Float(), 'f', -1, rv.Type().Bits())), nil
|
|
|
|
// case reflect.Slice:
|
|
// if rv.IsNil() {
|
|
// return []byte{}, nil
|
|
// }
|
|
// if rv.Type().Elem().Kind() == reflect.Uint8 {
|
|
// return rv.Bytes(), nil
|
|
// }
|
|
//
|
|
}
|
|
|
|
return t.Value.MarshalText()
|
|
}
|
|
*/
|
|
|
|
// SetValid changes this Value's value and sets it to be non-null.
|
|
func (t *Value[T]) SetValid(v T) {
|
|
t.V = v
|
|
t.Valid = true
|
|
}
|
|
|
|
// Ptr returns a pointer to this Value's value, or a nil pointer if this Value is null.
|
|
func (t Value[T]) Ptr() *T {
|
|
if !t.Valid {
|
|
return nil
|
|
}
|
|
return &t.V
|
|
}
|
|
|
|
// IsZero returns true for invalid Values, hopefully for future omitempty support.
|
|
// A non-null Value with a zero value will not be considered zero.
|
|
func (t Value[T]) IsZero() bool {
|
|
return !t.Valid
|
|
}
|
|
|
|
/*
|
|
// Equal returns true if both Value objects encode the same value or are both null.
|
|
func (t Value[T]) Equal(other Value[T]) bool {
|
|
return t.Valid == other.Valid && (t.V == other.V)
|
|
}
|
|
*/
|