Skip to content

Commit

Permalink
🐛 fix: panic occurs when passing nil in sub-slice. see issues #223
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Dec 13, 2023
1 parent c9a3e1a commit cf5c4c1
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 44 deletions.
20 changes: 12 additions & 8 deletions data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/gookit/filter"
"github.com/gookit/goutil/maputil"
"github.com/gookit/goutil/reflects"
"github.com/gookit/goutil/strutil"
)

Expand Down Expand Up @@ -669,18 +670,21 @@ func (d *StructData) Set(field string, val any) (newVal any, err error) {
}

// try manual convert type
srcKind, err := basicKindV2(rftVal.Kind())
if err != nil {
return nil, err
}
// srcKind, err := basicKindV2(rftVal.Kind())
// if err != nil {
// return nil, err
// }
// newVal, err = convTypeByBaseKind(val, srcKind, fv.Kind())

newVal, err = convTypeByBaseKind(val, srcKind, fv.Kind())
if err != nil {
return nil, err
// try manual convert type
newRv, err1 := reflects.ValueByKind(val, fv.Kind())
if err1 != nil {
return nil, err1
}

// update field value
fv.Set(reflect.ValueOf(newVal))
// fv.Set(reflect.ValueOf(newVal))
fv.Set(newRv)
return
}

Expand Down
70 changes: 62 additions & 8 deletions issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestIssue_2(t *testing.T) {
// type is error
err = v.Set("A", "abc")
assert.Error(t, err)
assert.Equal(t, validate.ErrConvertFail.Error(), err.Error())
assert.ErrMsg(t, err, `strconv.ParseFloat: parsing "abc": invalid syntax`)
}

// https://github.com/gookit/validate/issues/19
Expand Down Expand Up @@ -1294,6 +1294,47 @@ func TestIssues_206(t *testing.T) {
assert.StrContains(t, v.Errors.String(), "full_url: origins.*.name must be a valid full URL address")
}

// https://github.com/gookit/validate/issues/209
func TestIssues_209(t *testing.T) {
mp := make(map[string]any)
err := jsonutil.DecodeString(`{
"em": {
"code": "001",
"encounter_uid": 1,
"work_item_uid": 2,
"billing_provider": "Test provider",
"resident_provider": "Test Resident Provider"
},
"cpt": [
{
"code": "001",
"billing_provider": "Test provider",
"resident_provider": "Test Resident Provider"
},
{
"code": "OBS01",
"billing_provider": "Test provider",
"resident_provider": "Test Resident Provider"
},
{
"code": "SU002",
"resident_provider": "Test Resident Provider"
}
]
}`, &mp)
assert.NoErr(t, err)

v := validate.Map(mp)
v.StopOnError = false
v.StringRule("cpt.*.encounter", "required|int")
v.StringRule("cpt.*.billing_provider", "required")

assert.False(t, v.Validate())
// fmt.Println(v.Errors)
assert.StrContains(t, v.Errors.String(), "cpt.*.encounter is required to not be empty")
assert.StrContains(t, v.Errors.String(), "cpt.*.billing_provider is required to not be empty")
}

// https://github.com/gookit/validate/issues/213
func TestIssues_213(t *testing.T) {
type Person struct {
Expand Down Expand Up @@ -1404,23 +1445,36 @@ func TestIssues_223(t *testing.T) {
"clinics": []map[string]any{
{
"clinic_id": nil,
"doctors": []map[string]any{
{
"duration": nil,
},
},
},
},
}

v := validate.Map(m)

v.StringRule("clinics", "required|array")
v.StringRule("clinics.*.clinic_id", "string")

if !assert.False(t, v.Validate()) { // validate ok
safeData := v.SafeData()

fmt.Println("Validation OK:")
dump.Println(safeData)
} else {
if assert.False(t, v.Validate()) {
fmt.Println("Validation Fail:")
fmt.Println(string(v.Errors.JSON())) // all error messages
assert.StrContains(t, v.Errors.String(), "clinics.*.clinic_id value must be a string")
} else {
safeData := v.SafeData()
fmt.Println("Validation OK:")
dump.Println(safeData)
}

// test field in sub slice
v = validate.Map(m)

v.StringRule("clinics", "required|array")
v.StringRule("clinics.*.doctors.*.duration", "int")
assert.False(t, v.Validate())
s := v.Errors.String()
fmt.Println(s)
assert.StrContains(t, s, "clinics.*.doctors.*.duration value must be an integer")
}
38 changes: 10 additions & 28 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,15 @@ var anyType = reflect.TypeOf((*any)(nil)).Elem()
//
// FlatSlice([]any{ []any{3, 4}, []any{5, 6} }, 1) // Output: []any{3, 4, 5, 6}
//
// always return reflect.Value of []any. note: maybe flatSl.Cap != flatSl.Len
// always return reflect.Value of []any. NOTE: maybe flatSl.Cap != flatSl.Len
func flatSlice(sl reflect.Value, depth int) reflect.Value {
items := make([]reflect.Value, 0, sl.Cap())
slCap := addSliceItem(sl, depth, func(item reflect.Value) {
items = append(items, item)
if item.IsValid() {
items = append(items, item)
} else { // fix: if sub elem is nil, item will an invalid kind.
items = append(items, NilValue)
}
})

flatSl := reflect.MakeSlice(reflect.SliceOf(anyType), 0, slCap)
Expand Down Expand Up @@ -255,33 +259,11 @@ func getVariadicKind(typ reflect.Type) reflect.Kind {
//
//nolint:forcetypeassert
func convTypeByBaseKind(srcVal any, srcKind kind, dstType reflect.Kind) (any, error) {

Check warning on line 261 in util.go

View workflow job for this annotation

GitHub Actions / linter

unused-parameter: parameter 'srcKind' seems to be unused, consider removing or renaming it as _ (revive)
switch srcKind {
case stringKind:
switch dstType {
case reflect.Int:
return mathutil.ToInt(srcVal)
case reflect.Int64:
return mathutil.ToInt64(srcVal)
case reflect.Bool:
return strutil.Bool(srcVal.(string))
case reflect.String:
return srcVal.(string), nil
}
case intKind, uintKind:
i64 := mathutil.SafeInt64(srcVal)
switch dstType {
case reflect.Int64:
return i64, nil
case reflect.String:
return strutil.ToString(srcVal)
}
default:
switch dstType {
case reflect.String:
return strutil.ToString(srcVal)
}
rv, err := reflects.ConvToKind(srcVal, dstType)
if err != nil {
return nil, err
}
return nil, ErrConvertFail
return rv.Interface(), nil
}

// convert custom type to generic basic int, string, unit.
Expand Down

0 comments on commit cf5c4c1

Please sign in to comment.