add time support
This commit is contained in:
parent
b0ff605b25
commit
0a5ab05c88
4
field.go
4
field.go
@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/iancoleman/strcase"
|
"github.com/iancoleman/strcase"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pathString(f Field) string {
|
func pathString(f Field) string {
|
||||||
@ -298,7 +298,7 @@ func trimNameAndPath(name string, values []Field) []Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(v[i].name, fmt.Sprintf("%s-", name)) {
|
if strings.HasPrefix(v[i].name, fmt.Sprintf("%s-", name)) {
|
||||||
v[i].name = v[i].name[len(name) + 1:]
|
v[i].name = v[i].name[len(name)+1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -279,7 +279,10 @@ func TestField(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("bind fields", func(t *testing.T) {
|
t.Run("bind fields", func(t *testing.T) {
|
||||||
t.Run("no circular receiver", func(t *testing.T) {
|
t.Run("no circular receiver", func(t *testing.T) {
|
||||||
type s struct{Foo *s; Bar int}
|
type s struct {
|
||||||
|
Foo *s
|
||||||
|
Bar int
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
if len(bind.BindFields(&v, bind.NamedValue("bar", 42))) != 1 {
|
if len(bind.BindFields(&v, bind.NamedValue("bar", 42))) != 1 {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
@ -287,7 +290,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("no circular valeu", func(t *testing.T) {
|
t.Run("no circular valeu", func(t *testing.T) {
|
||||||
type s struct{Foo int}
|
type s struct{ Foo int }
|
||||||
type p *p
|
type p *p
|
||||||
var v p
|
var v p
|
||||||
if len(bind.BindFields(&v, bind.NamedValue("foo", 42))) != 1 {
|
if len(bind.BindFields(&v, bind.NamedValue("foo", 42))) != 1 {
|
||||||
@ -296,7 +299,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("set by name", func(t *testing.T) {
|
t.Run("set by name", func(t *testing.T) {
|
||||||
type s struct{FooBar int}
|
type s struct{ FooBar int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
|
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
|
||||||
if len(u) != 0 || v.FooBar != 42 {
|
if len(u) != 0 || v.FooBar != 42 {
|
||||||
@ -305,7 +308,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("set by path", func(t *testing.T) {
|
t.Run("set by path", func(t *testing.T) {
|
||||||
type s struct{FooBar int}
|
type s struct{ FooBar int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(&v, bind.ValueByPath([]string{"FooBar"}, 42))
|
u := bind.BindFields(&v, bind.ValueByPath([]string{"FooBar"}, 42))
|
||||||
if len(u) != 0 || v.FooBar != 42 {
|
if len(u) != 0 || v.FooBar != 42 {
|
||||||
@ -314,7 +317,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fail to bind", func(t *testing.T) {
|
t.Run("fail to bind", func(t *testing.T) {
|
||||||
type s struct{Foo int; Bar int}
|
type s struct {
|
||||||
|
Foo int
|
||||||
|
Bar int
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -328,7 +334,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("bind list", func(t *testing.T) {
|
t.Run("bind list", func(t *testing.T) {
|
||||||
type s struct{Foo []int}
|
type s struct{ Foo []int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -343,7 +349,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("bind list of structs", func(t *testing.T) {
|
t.Run("bind list of structs", func(t *testing.T) {
|
||||||
type s struct{Foo []struct{Bar int}}
|
type s struct{ Foo []struct{ Bar int } }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -358,7 +364,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("bind list in list", func(t *testing.T) {
|
t.Run("bind list in list", func(t *testing.T) {
|
||||||
type s struct{Foo []struct{Bar []int}}
|
type s struct{ Foo []struct{ Bar []int } }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -376,7 +382,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("list receiver", func(t *testing.T) {
|
t.Run("list receiver", func(t *testing.T) {
|
||||||
var l []struct{Foo int}
|
var l []struct{ Foo int }
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&l,
|
&l,
|
||||||
bind.NamedValue("foo", 21),
|
bind.NamedValue("foo", 21),
|
||||||
@ -391,8 +397,8 @@ func TestField(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("list short and cannot be set", func(t *testing.T) {
|
t.Run("list short and cannot be set", func(t *testing.T) {
|
||||||
type (
|
type (
|
||||||
s0 struct{Bar int}
|
s0 struct{ Bar int }
|
||||||
s1 struct{Foo []s0}
|
s1 struct{ Foo []s0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
v := s1{[]s0{{1}, {2}}}
|
v := s1{[]s0{{1}, {2}}}
|
||||||
@ -410,8 +416,8 @@ func TestField(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("list short and gets reset", func(t *testing.T) {
|
t.Run("list short and gets reset", func(t *testing.T) {
|
||||||
type (
|
type (
|
||||||
s0 struct{Bar int}
|
s0 struct{ Bar int }
|
||||||
s1 struct{Foo []s0}
|
s1 struct{ Foo []s0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
v := s1{[]s0{{1}, {2}}}
|
v := s1{[]s0{{1}, {2}}}
|
||||||
@ -429,8 +435,8 @@ func TestField(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("list has invalid type", func(t *testing.T) {
|
t.Run("list has invalid type", func(t *testing.T) {
|
||||||
type (
|
type (
|
||||||
s0 struct{Bar chan int}
|
s0 struct{ Bar chan int }
|
||||||
s1 struct{Foo []s0}
|
s1 struct{ Foo []s0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
v := s1{[]s0{{nil}, {nil}}}
|
v := s1{[]s0{{nil}, {nil}}}
|
||||||
@ -508,7 +514,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("allocate scalar map", func(t *testing.T) {
|
t.Run("allocate scalar map", func(t *testing.T) {
|
||||||
type s struct{Foo map[string]int}
|
type s struct{ Foo map[string]int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
|
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
|
||||||
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["bar"] != 42 {
|
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["bar"] != 42 {
|
||||||
@ -517,7 +523,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("scalar map addressing via path", func(t *testing.T) {
|
t.Run("scalar map addressing via path", func(t *testing.T) {
|
||||||
type s struct{Foo map[string]int}
|
type s struct{ Foo map[string]int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(&v, bind.ValueByPath([]string{"Foo", "Bar"}, 42))
|
u := bind.BindFields(&v, bind.ValueByPath([]string{"Foo", "Bar"}, 42))
|
||||||
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["Bar"] != 42 {
|
if len(u) != 0 || len(v.Foo) != 1 || v.Foo["Bar"] != 42 {
|
||||||
@ -534,7 +540,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("scalar map cannot be set", func(t *testing.T) {
|
t.Run("scalar map cannot be set", func(t *testing.T) {
|
||||||
type s struct{Foo map[string]int}
|
type s struct{ Foo map[string]int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
|
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
|
||||||
if len(u) != 1 {
|
if len(u) != 1 {
|
||||||
@ -551,7 +557,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("struct fields", func(t *testing.T) {
|
t.Run("struct fields", func(t *testing.T) {
|
||||||
type s struct{Foo int; Bar struct { Baz string }}
|
type s struct {
|
||||||
|
Foo int
|
||||||
|
Bar struct{ Baz string }
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -565,7 +574,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("non-existing field", func(t *testing.T) {
|
t.Run("non-existing field", func(t *testing.T) {
|
||||||
type s struct{Foo int; Bar struct { Baz string }}
|
type s struct {
|
||||||
|
Foo int
|
||||||
|
Bar struct{ Baz string }
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -579,7 +591,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("too many fields", func(t *testing.T) {
|
t.Run("too many fields", func(t *testing.T) {
|
||||||
type s struct{Foo int; Bar struct { Baz string }}
|
type s struct {
|
||||||
|
Foo int
|
||||||
|
Bar struct{ Baz string }
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -594,7 +609,11 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("pointer fields", func(t *testing.T) {
|
t.Run("pointer fields", func(t *testing.T) {
|
||||||
type s struct{Foo *int; Bar *struct { Baz *string }; Qux *[]struct{Quux string}}
|
type s struct {
|
||||||
|
Foo *int
|
||||||
|
Bar *struct{ Baz *string }
|
||||||
|
Qux *[]struct{ Quux string }
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -609,7 +628,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("unsupported pointer fields", func(t *testing.T) {
|
t.Run("unsupported pointer fields", func(t *testing.T) {
|
||||||
type s struct{Foo *int; Bar *chan int}
|
type s struct {
|
||||||
|
Foo *int
|
||||||
|
Bar *chan int
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -624,7 +646,10 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("struct fields by path", func(t *testing.T) {
|
t.Run("struct fields by path", func(t *testing.T) {
|
||||||
type s struct{Foo int; Bar struct { Baz string }}
|
type s struct {
|
||||||
|
Foo int
|
||||||
|
Bar struct{ Baz string }
|
||||||
|
}
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(
|
u := bind.BindFields(
|
||||||
&v,
|
&v,
|
||||||
@ -638,7 +663,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("cannot set field", func(t *testing.T) {
|
t.Run("cannot set field", func(t *testing.T) {
|
||||||
type s struct{Foo int}
|
type s struct{ Foo int }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(v, bind.NamedValue("foo", 42))
|
u := bind.BindFields(v, bind.NamedValue("foo", 42))
|
||||||
if len(u) != 1 {
|
if len(u) != 1 {
|
||||||
@ -648,8 +673,8 @@ func TestField(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("struct with anonymous field", func(t *testing.T) {
|
t.Run("struct with anonymous field", func(t *testing.T) {
|
||||||
type (
|
type (
|
||||||
s0 struct{Foo int}
|
s0 struct{ Foo int }
|
||||||
s1 struct{s0}
|
s1 struct{ s0 }
|
||||||
)
|
)
|
||||||
|
|
||||||
var v s1
|
var v s1
|
||||||
@ -660,7 +685,7 @@ func TestField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("receiver cannot be set", func(t *testing.T) {
|
t.Run("receiver cannot be set", func(t *testing.T) {
|
||||||
type s struct{Foo *struct{Bar int}}
|
type s struct{ Foo *struct{ Bar int } }
|
||||||
var v s
|
var v s
|
||||||
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
|
u := bind.BindFields(v, bind.NamedValue("foo-bar", 42))
|
||||||
if len(u) != 1 {
|
if len(u) != 1 {
|
||||||
|
|||||||
8
lib.go
8
lib.go
@ -44,10 +44,10 @@ func (f Field) Name() string {
|
|||||||
return nameFromPath(f.path)
|
return nameFromPath(f.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Field) List() bool { return f.list }
|
func (f Field) List() bool { return f.list }
|
||||||
func (f Field) Bool() bool { return f.isBool }
|
func (f Field) Bool() bool { return f.isBool }
|
||||||
func (f Field) Free() bool { return f.free }
|
func (f Field) Free() bool { return f.free }
|
||||||
func (f Field) Value() any { return f.value }
|
func (f Field) Value() any { return f.value }
|
||||||
|
|
||||||
// it does not return fields with free keys, however, this should be obvious
|
// it does not return fields with free keys, however, this should be obvious
|
||||||
// non-struct and non-named map values return unnamed fields
|
// non-struct and non-named map values return unnamed fields
|
||||||
|
|||||||
@ -1,7 +1 @@
|
|||||||
add time and duration support
|
|
||||||
don't change the input value when bind returns false
|
|
||||||
track down the cases when reflect can panic
|
track down the cases when reflect can panic
|
||||||
test:
|
|
||||||
- repeated bindings
|
|
||||||
- cases of allocations
|
|
||||||
- preallocated and unallocated list sizes
|
|
||||||
|
|||||||
@ -219,7 +219,7 @@ func TestScalar(t *testing.T) {
|
|||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("value has circular reference", func(t *testing.T) {
|
t.Run("value has circular reference", func(t *testing.T) {
|
||||||
var v any
|
var v any
|
||||||
p := &v
|
p := &v
|
||||||
|
|||||||
48
scan.go
48
scan.go
@ -1,12 +1,36 @@
|
|||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var timeLayouts = []string{
|
||||||
|
time.RFC3339,
|
||||||
|
time.RFC3339Nano,
|
||||||
|
time.DateTime,
|
||||||
|
time.DateOnly,
|
||||||
|
time.TimeOnly,
|
||||||
|
time.RFC822,
|
||||||
|
time.RFC822Z,
|
||||||
|
time.RFC850,
|
||||||
|
time.RFC1123,
|
||||||
|
time.RFC1123Z,
|
||||||
|
time.ANSIC,
|
||||||
|
time.UnixDate,
|
||||||
|
time.RubyDate,
|
||||||
|
time.Kitchen,
|
||||||
|
time.Layout,
|
||||||
|
time.Stamp,
|
||||||
|
time.StampMilli,
|
||||||
|
time.StampMicro,
|
||||||
|
time.StampNano,
|
||||||
|
}
|
||||||
|
|
||||||
func intParse[T any](parse func(string, int, int) (T, error), s string, byteSize int) (T, error) {
|
func intParse[T any](parse func(string, int, int) (T, error), s string, byteSize int) (T, error) {
|
||||||
bitSize := byteSize * 8
|
bitSize := byteSize * 8
|
||||||
switch {
|
switch {
|
||||||
@ -38,6 +62,16 @@ func scanConvert(t reflect.Type, v any) (any, bool) {
|
|||||||
return r.Convert(t).Interface(), true
|
return r.Convert(t).Interface(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTime(s string) (any, error) {
|
||||||
|
for _, l := range timeLayouts {
|
||||||
|
if t, err := time.Parse(l, s); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}, errors.New("failed to parse time")
|
||||||
|
}
|
||||||
|
|
||||||
func scanString(t reflect.Type, s string) (any, bool) {
|
func scanString(t reflect.Type, s string) (any, bool) {
|
||||||
var (
|
var (
|
||||||
v any
|
v any
|
||||||
@ -48,7 +82,13 @@ func scanString(t reflect.Type, s string) (any, bool) {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
v, err = strconv.ParseBool(s)
|
v, err = strconv.ParseBool(s)
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
v, err = parseInt(s, int(t.Size()))
|
if isDuration(t) {
|
||||||
|
v, err = time.ParseDuration(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
v, err = parseInt(s, int(t.Size()))
|
||||||
|
}
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
v, err = parseUint(s, int(t.Size()))
|
v, err = parseUint(s, int(t.Size()))
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
@ -56,7 +96,11 @@ func scanString(t reflect.Type, s string) (any, bool) {
|
|||||||
case reflect.String:
|
case reflect.String:
|
||||||
v = s
|
v = s
|
||||||
default:
|
default:
|
||||||
return nil, false
|
if isTime(t) {
|
||||||
|
v, err = parseTime(s)
|
||||||
|
} else {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
50
scan_test.go
50
scan_test.go
@ -3,21 +3,10 @@ package bind_test
|
|||||||
import (
|
import (
|
||||||
"code.squareroundforest.org/arpio/bind"
|
"code.squareroundforest.org/arpio/bind"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestScan(t *testing.T) {
|
func TestScan(t *testing.T) {
|
||||||
// conversion
|
|
||||||
// unscannable
|
|
||||||
// bool
|
|
||||||
// int
|
|
||||||
// int sized
|
|
||||||
// uint
|
|
||||||
// uint sized
|
|
||||||
// binary
|
|
||||||
// octal
|
|
||||||
// hexa
|
|
||||||
// num parse failing
|
|
||||||
|
|
||||||
t.Run("conversion", func(t *testing.T) {
|
t.Run("conversion", func(t *testing.T) {
|
||||||
var i64 int64
|
var i64 int64
|
||||||
i := 42
|
i := 42
|
||||||
@ -117,4 +106,41 @@ func TestScan(t *testing.T) {
|
|||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("duration", func(t *testing.T) {
|
||||||
|
var d time.Duration
|
||||||
|
if !bind.BindScalar(&d, "9s") || d != 9*time.Second {
|
||||||
|
t.Fatal(d)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("duration convert", func(t *testing.T) {
|
||||||
|
type dur time.Duration
|
||||||
|
var d dur
|
||||||
|
if !bind.BindScalar(&d, "9s") || d != dur(9*time.Second) {
|
||||||
|
t.Fatal(d)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time rfc", func(t *testing.T) {
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, "2025-08-31T01:14:55+02:00") || tim.Year() != 2025 {
|
||||||
|
t.Fatal(tim)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time only", func(t *testing.T) {
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, "01:14:55") || tim.Second() != 55 {
|
||||||
|
t.Fatal(tim)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time convert", func(t *testing.T) {
|
||||||
|
type timey time.Time
|
||||||
|
var tim timey
|
||||||
|
if !bind.BindScalar(&tim, "2025-08-31T01:14:55+02:00") || time.Time(tim).Year() != 2025 {
|
||||||
|
t.Fatal(tim)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
15
type.go
15
type.go
@ -1,6 +1,9 @@
|
|||||||
package bind
|
package bind
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type unpackFlag int
|
type unpackFlag int
|
||||||
|
|
||||||
@ -123,6 +126,14 @@ func isInterface(t reflect.Type) bool {
|
|||||||
return t.Kind() == reflect.Interface && t.NumMethod() > 0
|
return t.Kind() == reflect.Interface && t.NumMethod() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isTime(t reflect.Type) bool {
|
||||||
|
return t.ConvertibleTo(reflect.TypeFor[time.Time]())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDuration(t reflect.Type) bool {
|
||||||
|
return t.ConvertibleTo(reflect.TypeFor[time.Duration]())
|
||||||
|
}
|
||||||
|
|
||||||
func isScalar(t reflect.Type) bool {
|
func isScalar(t reflect.Type) bool {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Bool,
|
case reflect.Bool,
|
||||||
@ -142,7 +153,7 @@ func isScalar(t reflect.Type) bool {
|
|||||||
reflect.String:
|
reflect.String:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return isTime(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user