From f3e17035cd36b4d53c4a916a3100f6087cbecc55 Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Thu, 4 Sep 2025 01:48:21 +0200 Subject: [PATCH] add bind scalar from field type --- lib.go | 13 +++--- scalar.go | 49 ++++++++++++++++++++- scalar_test.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 6 deletions(-) diff --git a/lib.go b/lib.go index 0bc665a..a888c76 100644 --- a/lib.go +++ b/lib.go @@ -18,12 +18,10 @@ const ( Uint16 FieldType = reflect.Uint16 Uint32 FieldType = reflect.Uint32 Uint64 FieldType = reflect.Uint64 - Uintptr FieldType = reflect.Uintptr Float32 FieldType = reflect.Float32 Float64 FieldType = reflect.Float64 String FieldType = reflect.String Any FieldType = reflect.Interface - Struct FieldType = reflect.Struct Duration FieldType = 0xfffe Time FieldType = 0xffff ) @@ -48,16 +46,21 @@ func BindScalar(receiver any, value ...any) bool { return bindScalarReflect(receiver, value) } -// BindScalarCreate is like BindScalar, but it allocates the receiver from the type T. +// CreateAndBindScalar is like BindScalar, but it allocates the receiver from the type T. func CreateAndBindScalar[T any](value ...any) (T, bool) { return bindScalarCreateReflect[T](value) } -// BindScalarCreate is like BindScalar, but it allocates the receiver from the type t. +// CreateAndBindScalarFor is like BindScalar, but it allocates the receiver from the type t. func CreateAndBindScalarFor(t reflect.Type, value ...any) (reflect.Value, bool) { return bindScalarCreate(t, value) } +// CreateAndBindScalarFieldType is like BindScalar, but it allocates the receiver from the field type t. +func CreateAndBindScalarFieldType(t FieldType, value ...any) (reflect.Value, bool) { + return bindScalarCreateFieldType(t, value) +} + // ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact // field path in the receiver structure. func ValueByPath(path []string, value any) Field { @@ -145,7 +148,7 @@ func CreateAndBindFor(t reflect.Type, value ...Field) (reflect.Value, []Field) { return bindFieldsCreate(t, value) } -// AcceptsScalar checks if a type can be used with BindScalarCreate or the values of the type with BindScalar. +// AcceptsScalar checks if a type can be used with CreateScalarAndBind or the values of the type with BindScalar. func AcceptsScalar[T any]() bool { return acceptsScalarReflect[T]() } diff --git a/scalar.go b/scalar.go index 503fac7..96aadb4 100644 --- a/scalar.go +++ b/scalar.go @@ -1,6 +1,9 @@ package bind -import "reflect" +import ( + "reflect" + "time" +) func bindScalar(receiver reflect.Value, values []any) bool { if !receiver.IsValid() || len(values) == 0 { @@ -107,3 +110,47 @@ func bindScalarCreateReflect[T any](values []any) (T, bool) { return v.Interface().(T), true } + +func bindScalarCreateFieldType(t FieldType, values []any) (reflect.Value, bool) { + var r reflect.Type + switch t { + case Bool: + r = reflect.TypeFor[bool]() + case Int: + r = reflect.TypeFor[int]() + case Int8: + r = reflect.TypeFor[int8]() + case Int16: + r = reflect.TypeFor[int16]() + case Int32: + r = reflect.TypeFor[int32]() + case Int64: + r = reflect.TypeFor[int64]() + case Uint: + r = reflect.TypeFor[uint]() + case Uint8: + r = reflect.TypeFor[uint8]() + case Uint16: + r = reflect.TypeFor[uint16]() + case Uint32: + r = reflect.TypeFor[uint32]() + case Uint64: + r = reflect.TypeFor[uint64]() + case Float32: + r = reflect.TypeFor[float32]() + case Float64: + r = reflect.TypeFor[float64]() + case String: + r = reflect.TypeFor[string]() + case Any: + r = reflect.TypeFor[any]() + case Duration: + r = reflect.TypeFor[time.Duration]() + case Time: + r = reflect.TypeFor[time.Time]() + default: + return reflect.Value{}, false + } + + return bindScalarCreate(r, values) +} diff --git a/scalar_test.go b/scalar_test.go index 04a7fcc..c159611 100644 --- a/scalar_test.go +++ b/scalar_test.go @@ -5,6 +5,7 @@ import ( "reflect" "slices" "testing" + "time" ) type valuer interface { @@ -258,4 +259,120 @@ func TestScalar(t *testing.T) { t.Fatal() } }) + + t.Run("bind from field type", func(t *testing.T) { + t.Run("bool", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Bool, true); !ok || !v.Interface().(bool) { + t.Fatal() + } + }) + + t.Run("int", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Int, 42); !ok || v.Interface().(int) != 42 { + t.Fatal() + } + }) + + t.Run("int8", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Int8, 42); !ok || v.Interface().(int8) != 42 { + t.Fatal() + } + }) + + t.Run("int16", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Int16, 42); !ok || v.Interface().(int16) != 42 { + t.Fatal() + } + }) + + t.Run("int32", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Int32, 42); !ok || v.Interface().(int32) != 42 { + t.Fatal() + } + }) + + t.Run("int64", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Int64, 42); !ok || v.Interface().(int64) != 42 { + t.Fatal() + } + }) + + t.Run("uint", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Uint, 42); !ok || v.Interface().(uint) != 42 { + t.Fatal() + } + }) + + t.Run("uint8", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Uint8, 42); !ok || v.Interface().(uint8) != 42 { + t.Fatal() + } + }) + + t.Run("uint16", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Uint16, 42); !ok || v.Interface().(uint16) != 42 { + t.Fatal() + } + }) + + t.Run("uint32", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Uint32, 42); !ok || v.Interface().(uint32) != 42 { + t.Fatal() + } + }) + + t.Run("uint64", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Uint64, 42); !ok || v.Interface().(uint64) != 42 { + t.Fatal() + } + }) + + t.Run("float32", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Float32, 42); !ok || v.Interface().(float32) != 42 { + t.Fatal() + } + }) + + t.Run("float64", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Float64, 42); !ok || v.Interface().(float64) != 42 { + t.Fatal() + } + }) + + t.Run("string", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.String, "foo"); !ok || v.Interface().(string) != "foo" { + t.Fatal() + } + }) + + t.Run("any", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Any, 42); !ok || v.Interface() != 42 { + t.Fatal() + } + }) + + t.Run("duration", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Duration, time.Second); !ok || v.Interface().(time.Duration) != time.Second { + t.Fatal() + } + }) + + t.Run("time", func(t *testing.T) { + if v, ok := bind.CreateAndBindScalarFieldType(bind.Time, "14:22:45"); !ok || v.Interface().(time.Time).Hour() != 14 { + t.Fatal() + } + }) + + t.Run("fail", func(t *testing.T) { + if _, ok := bind.CreateAndBindScalarFieldType(bind.Int, "foo"); ok { + t.Fatal() + } + }) + + t.Run("unsupported type", func(t *testing.T) { + if _, ok := bind.CreateAndBindScalarFieldType(bind.Duration*bind.Time, "foo"); ok { + t.Fatal() + } + }) + }) }