github.com/go-playground/pkg/v5@v5.29.1/values/option/option_sql_go1.22.go (about)

     1  //go:build go1.22
     2  
     3  package optionext
     4  
     5  import (
     6  	"database/sql"
     7  	"database/sql/driver"
     8  	"encoding/json"
     9  	"fmt"
    10  	"math"
    11  	"reflect"
    12  	"time"
    13  )
    14  
    15  var (
    16  	scanType      = reflect.TypeFor[sql.Scanner]()
    17  	valuerType    = reflect.TypeFor[driver.Valuer]()
    18  	byteSliceType = reflect.TypeFor[[]byte]()
    19  	timeType      = reflect.TypeFor[time.Time]()
    20  	stringType    = reflect.TypeFor[string]()
    21  	int64Type     = reflect.TypeFor[int64]()
    22  	float64Type   = reflect.TypeFor[float64]()
    23  	boolType      = reflect.TypeFor[bool]()
    24  )
    25  
    26  // Value implements the driver.Valuer interface.
    27  //
    28  // This honours the `driver.Valuer` interface if the value implements it.
    29  // It also supports custom types of the std types and treats all else as []byte
    30  func (o Option[T]) Value() (driver.Value, error) {
    31  	if o.IsNone() {
    32  		return nil, nil
    33  	}
    34  	val := reflect.ValueOf(o.value)
    35  
    36  	if val.Type().Implements(valuerType) {
    37  		return val.Interface().(driver.Valuer).Value()
    38  	}
    39  	switch val.Kind() {
    40  	case reflect.String:
    41  		return val.Convert(stringType).Interface(), nil
    42  	case reflect.Bool:
    43  		return val.Convert(boolType).Interface(), nil
    44  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    45  		return val.Convert(int64Type).Interface(), nil
    46  	case reflect.Float64:
    47  		return val.Convert(float64Type).Interface(), nil
    48  	case reflect.Slice, reflect.Array:
    49  		if val.Type().ConvertibleTo(byteSliceType) {
    50  			return val.Convert(byteSliceType).Interface(), nil
    51  		}
    52  		return json.Marshal(val.Interface())
    53  	case reflect.Struct:
    54  		if val.CanConvert(timeType) {
    55  			return val.Convert(timeType).Interface(), nil
    56  		}
    57  		return json.Marshal(val.Interface())
    58  	case reflect.Map:
    59  		return json.Marshal(val.Interface())
    60  	default:
    61  		return o.value, nil
    62  	}
    63  }
    64  
    65  // Scan implements the sql.Scanner interface.
    66  func (o *Option[T]) Scan(value any) error {
    67  
    68  	if value == nil {
    69  		*o = None[T]()
    70  		return nil
    71  	}
    72  
    73  	val := reflect.ValueOf(&o.value)
    74  
    75  	if val.Type().Implements(scanType) {
    76  		err := val.Interface().(sql.Scanner).Scan(value)
    77  		if err != nil {
    78  			return err
    79  		}
    80  		o.isSome = true
    81  		return nil
    82  	}
    83  
    84  	val = val.Elem()
    85  
    86  	switch val.Kind() {
    87  	case reflect.String, reflect.Bool, reflect.Uint8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64:
    88  		var v sql.Null[T]
    89  		if err := v.Scan(value); err != nil {
    90  			return err
    91  		}
    92  		*o = Some(reflect.ValueOf(v.V).Convert(val.Type()).Interface().(T))
    93  	case reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
    94  		v := reflect.ValueOf(value)
    95  		if v.Type().ConvertibleTo(val.Type()) {
    96  			*o = Some(reflect.ValueOf(v.Convert(val.Type()).Interface()).Interface().(T))
    97  		} else {
    98  			return fmt.Errorf("value %T not convertable to %T", value, o.value)
    99  		}
   100  	case reflect.Float32:
   101  		var v sql.Null[float64]
   102  		if err := v.Scan(value); err != nil {
   103  			return err
   104  		}
   105  		*o = Some(reflect.ValueOf(v.V).Convert(val.Type()).Interface().(T))
   106  	case reflect.Int:
   107  		var v sql.Null[int64]
   108  		if err := v.Scan(value); err != nil {
   109  			return err
   110  		}
   111  		if v.V > math.MaxInt || v.V < math.MinInt {
   112  			return fmt.Errorf("value %d out of range for int", v.V)
   113  		}
   114  		*o = Some(reflect.ValueOf(v.V).Convert(val.Type()).Interface().(T))
   115  	case reflect.Int8:
   116  		var v sql.Null[int64]
   117  		if err := v.Scan(value); err != nil {
   118  			return err
   119  		}
   120  		if v.V > math.MaxInt8 || v.V < math.MinInt8 {
   121  			return fmt.Errorf("value %d out of range for int8", v.V)
   122  		}
   123  		*o = Some(reflect.ValueOf(v.V).Convert(val.Type()).Interface().(T))
   124  	case reflect.Interface:
   125  		*o = Some(reflect.ValueOf(value).Convert(val.Type()).Interface().(T))
   126  	case reflect.Struct:
   127  		if val.CanConvert(timeType) {
   128  			switch t := value.(type) {
   129  			case string:
   130  				tm, err := time.Parse(time.RFC3339Nano, t)
   131  				if err != nil {
   132  					return err
   133  				}
   134  				*o = Some(reflect.ValueOf(tm).Convert(val.Type()).Interface().(T))
   135  
   136  			case []byte:
   137  				tm, err := time.Parse(time.RFC3339Nano, string(t))
   138  				if err != nil {
   139  					return err
   140  				}
   141  				*o = Some(reflect.ValueOf(tm).Convert(val.Type()).Interface().(T))
   142  
   143  			default:
   144  				var v sql.Null[time.Time]
   145  				if err := v.Scan(value); err != nil {
   146  					return err
   147  				}
   148  				*o = Some(reflect.ValueOf(v.V).Convert(val.Type()).Interface().(T))
   149  			}
   150  			return nil
   151  		}
   152  		fallthrough
   153  
   154  	default:
   155  		switch val.Kind() {
   156  		case reflect.Struct, reflect.Slice, reflect.Map:
   157  			v := reflect.ValueOf(value)
   158  
   159  			if v.Type().ConvertibleTo(byteSliceType) {
   160  				if val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 {
   161  					*o = Some(reflect.ValueOf(v.Convert(val.Type()).Interface()).Interface().(T))
   162  				} else {
   163  					if err := json.Unmarshal(v.Convert(byteSliceType).Interface().([]byte), &o.value); err != nil {
   164  						return err
   165  					}
   166  				}
   167  				o.isSome = true
   168  				return nil
   169  			}
   170  		}
   171  		return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", value, o.value)
   172  	}
   173  	return nil
   174  }