mirror of https://github.com/gofiber/fiber.git
* 🩹 fix: make SetValWithStruct set zero values and support more types * 🚨 test: check zero in int_slice * fix: SetValWithStruct does not exist in fiber v2 * 🩹fix: restrict supported types in SetValWithStruct * 🩹fix: golangci-lint --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>pull/3244/head^2
parent
a63bd340a4
commit
1134e1f408
|
@ -1062,11 +1062,14 @@ func ReleaseFile(f *File) {
|
|||
filePool.Put(f)
|
||||
}
|
||||
|
||||
// SetValWithStruct Set some values using structs.
|
||||
// `p` is a structure that implements the WithStruct interface,
|
||||
// The field name can be specified by `tagName`.
|
||||
// `v` is a struct include some data.
|
||||
// Note: This method only supports simple types and nested structs are not currently supported.
|
||||
// SetValWithStruct stores the fields of `v` into `p`.
|
||||
// `tagName` specifies the key used to store into `p`. If not specified,
|
||||
// the field name is used by default.
|
||||
// `v` is a struct or a pointer to a struct containing some data.
|
||||
// Fields in `v` should be string, int, int8, int16, int32, int64, uint,
|
||||
// uint8, uint16, uint32, uint64, float32, float64, complex64,
|
||||
// complex128 or bool. Arrays or slices are inserted sequentially with the
|
||||
// same key. Other types are ignored.
|
||||
func SetValWithStruct(p WithStruct, tagName string, v any) {
|
||||
valueOfV := reflect.ValueOf(v)
|
||||
typeOfV := reflect.TypeOf(v)
|
||||
|
@ -1080,25 +1083,31 @@ func SetValWithStruct(p WithStruct, tagName string, v any) {
|
|||
}
|
||||
|
||||
// Boring type judge.
|
||||
// TODO: cover more types and complex data structure.
|
||||
var setVal func(name string, value reflect.Value)
|
||||
var setVal func(name string, val reflect.Value)
|
||||
setVal = func(name string, val reflect.Value) {
|
||||
switch val.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
p.Add(name, strconv.Itoa(int(val.Int())))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
p.Add(name, strconv.FormatUint(val.Uint(), 10))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p.Add(name, strconv.FormatFloat(val.Float(), 'f', -1, 64))
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
p.Add(name, strconv.FormatComplex(val.Complex(), 'f', -1, 128))
|
||||
case reflect.Bool:
|
||||
if val.Bool() {
|
||||
p.Add(name, "true")
|
||||
} else {
|
||||
p.Add(name, "false")
|
||||
}
|
||||
case reflect.String:
|
||||
p.Add(name, val.String())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p.Add(name, strconv.FormatFloat(val.Float(), 'f', -1, 64))
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
setVal(name, val.Index(i))
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1113,9 +1122,6 @@ func SetValWithStruct(p WithStruct, tagName string, v any) {
|
|||
name = field.Name
|
||||
}
|
||||
val := valueOfV.Field(i)
|
||||
if val.IsZero() {
|
||||
continue
|
||||
}
|
||||
// To cover slice and array, we delete the val then add it.
|
||||
p.Del(name)
|
||||
setVal(name, val)
|
||||
|
|
|
@ -1530,14 +1530,16 @@ func Test_Request_MaxRedirects(t *testing.T) {
|
|||
func Test_SetValWithStruct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// test SetValWithStruct vai QueryParam struct.
|
||||
// test SetValWithStruct via QueryParam struct.
|
||||
type args struct {
|
||||
TString string
|
||||
TSlice []string
|
||||
TIntSlice []int `param:"int_slice"`
|
||||
unexport int
|
||||
TInt int
|
||||
TUint uint
|
||||
TFloat float64
|
||||
TComplex complex128
|
||||
TBool bool
|
||||
}
|
||||
|
||||
|
@ -1550,18 +1552,22 @@ func Test_SetValWithStruct(t *testing.T) {
|
|||
SetValWithStruct(p, "param", args{
|
||||
unexport: 5,
|
||||
TInt: 5,
|
||||
TUint: 5,
|
||||
TString: "string",
|
||||
TFloat: 3.1,
|
||||
TComplex: 3 + 4i,
|
||||
TBool: false,
|
||||
TSlice: []string{"foo", "bar"},
|
||||
TIntSlice: []int{1, 2},
|
||||
TIntSlice: []int{0, 1, 2},
|
||||
})
|
||||
|
||||
require.Equal(t, "", string(p.Peek("unexport")))
|
||||
require.Equal(t, []byte("5"), p.Peek("TInt"))
|
||||
require.Equal(t, []byte("5"), p.Peek("TUint"))
|
||||
require.Equal(t, []byte("string"), p.Peek("TString"))
|
||||
require.Equal(t, []byte("3.1"), p.Peek("TFloat"))
|
||||
require.Equal(t, "", string(p.Peek("TBool")))
|
||||
require.Equal(t, []byte("(3+4i)"), p.Peek("TComplex"))
|
||||
require.Equal(t, []byte("false"), p.Peek("TBool"))
|
||||
require.True(t, func() bool {
|
||||
for _, v := range p.PeekMulti("TSlice") {
|
||||
if string(v) == "foo" {
|
||||
|
@ -1580,6 +1586,15 @@ func Test_SetValWithStruct(t *testing.T) {
|
|||
return false
|
||||
}())
|
||||
|
||||
require.True(t, func() bool {
|
||||
for _, v := range p.PeekMulti("int_slice") {
|
||||
if string(v) == "0" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}())
|
||||
|
||||
require.True(t, func() bool {
|
||||
for _, v := range p.PeekMulti("int_slice") {
|
||||
if string(v) == "1" {
|
||||
|
@ -1655,24 +1670,6 @@ func Test_SetValWithStruct(t *testing.T) {
|
|||
}())
|
||||
})
|
||||
|
||||
t.Run("the zero val should be ignore", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := &QueryParam{
|
||||
Args: fasthttp.AcquireArgs(),
|
||||
}
|
||||
SetValWithStruct(p, "param", &args{
|
||||
TInt: 0,
|
||||
TString: "",
|
||||
TFloat: 0.0,
|
||||
})
|
||||
|
||||
require.Equal(t, "", string(p.Peek("TInt")))
|
||||
require.Equal(t, "", string(p.Peek("TString")))
|
||||
require.Equal(t, "", string(p.Peek("TFloat")))
|
||||
require.Empty(t, p.PeekMulti("TSlice"))
|
||||
require.Empty(t, p.PeekMulti("int_slice"))
|
||||
})
|
||||
|
||||
t.Run("error type should ignore", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := &QueryParam{
|
||||
|
@ -1684,14 +1681,16 @@ func Test_SetValWithStruct(t *testing.T) {
|
|||
}
|
||||
|
||||
func Benchmark_SetValWithStruct(b *testing.B) {
|
||||
// test SetValWithStruct vai QueryParam struct.
|
||||
// test SetValWithStruct via QueryParam struct.
|
||||
type args struct {
|
||||
TString string
|
||||
TSlice []string
|
||||
TIntSlice []int `param:"int_slice"`
|
||||
unexport int
|
||||
TInt int
|
||||
TUint uint
|
||||
TFloat float64
|
||||
TComplex complex128
|
||||
TBool bool
|
||||
}
|
||||
|
||||
|
@ -1707,19 +1706,23 @@ func Benchmark_SetValWithStruct(b *testing.B) {
|
|||
SetValWithStruct(p, "param", args{
|
||||
unexport: 5,
|
||||
TInt: 5,
|
||||
TUint: 5,
|
||||
TString: "string",
|
||||
TFloat: 3.1,
|
||||
TComplex: 3 + 4i,
|
||||
TBool: false,
|
||||
TSlice: []string{"foo", "bar"},
|
||||
TIntSlice: []int{1, 2},
|
||||
TIntSlice: []int{0, 1, 2},
|
||||
})
|
||||
}
|
||||
|
||||
require.Equal(b, "", string(p.Peek("unexport")))
|
||||
require.Equal(b, []byte("5"), p.Peek("TInt"))
|
||||
require.Equal(b, []byte("5"), p.Peek("TUint"))
|
||||
require.Equal(b, []byte("string"), p.Peek("TString"))
|
||||
require.Equal(b, []byte("3.1"), p.Peek("TFloat"))
|
||||
require.Equal(b, "", string(p.Peek("TBool")))
|
||||
require.Equal(b, []byte("(3+4i)"), p.Peek("TComplex"))
|
||||
require.Equal(b, []byte("false"), p.Peek("TBool"))
|
||||
require.True(b, func() bool {
|
||||
for _, v := range p.PeekMulti("TSlice") {
|
||||
if string(v) == "foo" {
|
||||
|
@ -1738,6 +1741,15 @@ func Benchmark_SetValWithStruct(b *testing.B) {
|
|||
return false
|
||||
}())
|
||||
|
||||
require.True(b, func() bool {
|
||||
for _, v := range p.PeekMulti("int_slice") {
|
||||
if string(v) == "0" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}())
|
||||
|
||||
require.True(b, func() bool {
|
||||
for _, v := range p.PeekMulti("int_slice") {
|
||||
if string(v) == "1" {
|
||||
|
@ -1817,29 +1829,6 @@ func Benchmark_SetValWithStruct(b *testing.B) {
|
|||
}())
|
||||
})
|
||||
|
||||
b.Run("the zero val should be ignore", func(b *testing.B) {
|
||||
p := &QueryParam{
|
||||
Args: fasthttp.AcquireArgs(),
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
SetValWithStruct(p, "param", &args{
|
||||
TInt: 0,
|
||||
TString: "",
|
||||
TFloat: 0.0,
|
||||
})
|
||||
}
|
||||
|
||||
require.Empty(b, string(p.Peek("TInt")))
|
||||
require.Empty(b, string(p.Peek("TString")))
|
||||
require.Empty(b, string(p.Peek("TFloat")))
|
||||
require.Empty(b, p.PeekMulti("TSlice"))
|
||||
require.Empty(b, p.PeekMulti("int_slice"))
|
||||
})
|
||||
|
||||
b.Run("error type should ignore", func(b *testing.B) {
|
||||
p := &QueryParam{
|
||||
Args: fasthttp.AcquireArgs(),
|
||||
|
|
Loading…
Reference in New Issue