1
0

add bind scalar from field type

This commit is contained in:
Arpad Ryszka 2025-09-04 01:48:21 +02:00
parent 8683d8ba40
commit f3e17035cd
3 changed files with 173 additions and 6 deletions

13
lib.go
View File

@ -18,12 +18,10 @@ const (
Uint16 FieldType = reflect.Uint16 Uint16 FieldType = reflect.Uint16
Uint32 FieldType = reflect.Uint32 Uint32 FieldType = reflect.Uint32
Uint64 FieldType = reflect.Uint64 Uint64 FieldType = reflect.Uint64
Uintptr FieldType = reflect.Uintptr
Float32 FieldType = reflect.Float32 Float32 FieldType = reflect.Float32
Float64 FieldType = reflect.Float64 Float64 FieldType = reflect.Float64
String FieldType = reflect.String String FieldType = reflect.String
Any FieldType = reflect.Interface Any FieldType = reflect.Interface
Struct FieldType = reflect.Struct
Duration FieldType = 0xfffe Duration FieldType = 0xfffe
Time FieldType = 0xffff Time FieldType = 0xffff
) )
@ -48,16 +46,21 @@ func BindScalar(receiver any, value ...any) bool {
return bindScalarReflect(receiver, value) 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) { func CreateAndBindScalar[T any](value ...any) (T, bool) {
return bindScalarCreateReflect[T](value) 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) { func CreateAndBindScalarFor(t reflect.Type, value ...any) (reflect.Value, bool) {
return bindScalarCreate(t, value) 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 // ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact
// field path in the receiver structure. // field path in the receiver structure.
func ValueByPath(path []string, value any) Field { 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) 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 { func AcceptsScalar[T any]() bool {
return acceptsScalarReflect[T]() return acceptsScalarReflect[T]()
} }

View File

@ -1,6 +1,9 @@
package bind package bind
import "reflect" import (
"reflect"
"time"
)
func bindScalar(receiver reflect.Value, values []any) bool { func bindScalar(receiver reflect.Value, values []any) bool {
if !receiver.IsValid() || len(values) == 0 { if !receiver.IsValid() || len(values) == 0 {
@ -107,3 +110,47 @@ func bindScalarCreateReflect[T any](values []any) (T, bool) {
return v.Interface().(T), true 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)
}

View File

@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"slices" "slices"
"testing" "testing"
"time"
) )
type valuer interface { type valuer interface {
@ -258,4 +259,120 @@ func TestScalar(t *testing.T) {
t.Fatal() 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()
}
})
})
} }