diff --git a/go.mod b/go.mod index 3f8640f..f140395 100644 --- a/go.mod +++ b/go.mod @@ -12,5 +12,5 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index d413d0e..a52833a 100644 --- a/go.sum +++ b/go.sum @@ -17,4 +17,6 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/issues_test.go b/issues_test.go index 3fefa57..89f2c5d 100644 --- a/issues_test.go +++ b/issues_test.go @@ -1076,8 +1076,8 @@ func TestIssue_143(t *testing.T) { ok = v.Validate() dump.Println(v.Errors) - assert.False(t, ok) - assert.Equal(t, "age min value is 30", v.Errors.One()) + assert.True(t, ok) + assert.Equal(t, "", v.Errors.One()) } // https://github.com/gookit/validate/issues/148 diff --git a/util.go b/util.go index b9d2016..5d400ce 100644 --- a/util.go +++ b/util.go @@ -223,6 +223,8 @@ func CalcLength(val any) int { // // only check for: int(X), uint(X), float(X), string. func valueCompare(srcVal, dstVal any, op string) (ok bool) { + srcVal = indirectValue(srcVal) + // string compare if str1, ok := srcVal.(string); ok { str2, err := strutil.ToString(dstVal) @@ -536,6 +538,32 @@ func removeValuePtr(t reflect.Value) reflect.Value { return t } +func indirectValue(input any) any { + // Check if input is nil + if input == nil { + return nil + } + + // Use reflect to handle the value + val := reflect.ValueOf(input) + + // If the value is a pointer, then use reflect.Indirect to get the actual value it points to + if val.Kind() == reflect.Ptr { + // Use reflect.Indirect to safely dereference the pointer + val = reflect.Indirect(val) + + // If the dereferenced value is valid (not nil), return the interface + if val.IsValid() { + return val.Interface() + } + // If the dereferenced value is not valid, return nil + return nil + } + + // If the input is not a pointer, just return the input as it is + return input +} + // ---- From package "text/template" -> text/template/exec.go // indirect returns the item at the end of indirection, and a bool to indicate if it's nil. diff --git a/validating.go b/validating.go index e742da5..ccf9850 100644 --- a/validating.go +++ b/validating.go @@ -546,6 +546,11 @@ func callValidatorValue(fv reflect.Value, val any, args []any) bool { rftVal = nilRVal } + // Add this check to handle pointer values + if rftVal.Kind() == reflect.Ptr && !rftVal.IsNil() { + rftVal = rftVal.Elem() + } + argIn[0] = rftVal for i := 0; i < argNum; i++ { rftValA := reflect.ValueOf(args[i]) diff --git a/validators.go b/validators.go index 1bdaa17..08c17e2 100644 --- a/validators.go +++ b/validators.go @@ -517,6 +517,8 @@ func IsUint(val any) bool { // IsBool check. allow: bool, string. func IsBool(val any) bool { + val = indirectValue(val) + if _, ok := val.(bool); ok { return true } @@ -530,6 +532,8 @@ func IsBool(val any) bool { // IsFloat check. allow: floatX, string func IsFloat(val any) bool { + val = indirectValue(val) + if val == nil { return false } @@ -605,6 +609,8 @@ func IsMap(val any) (ok bool) { // IsInt check, and support length check func IsInt(val any, minAndMax ...int64) (ok bool) { + val = indirectValue(val) + if val == nil { return false } @@ -637,6 +643,8 @@ func IsInt(val any, minAndMax ...int64) (ok bool) { // ok := IsString(val, 5) // with min len check // ok := IsString(val, 5, 12) // with min and max len check func IsString(val any, minAndMaxLen ...int) (ok bool) { + val = indirectValue(val) + if val == nil { return false } @@ -790,6 +798,8 @@ func IsAlphaDash(s string) bool { // IsNumber string. should >= 0 func IsNumber(v any) bool { + v = indirectValue(v) + if v == nil { return false } @@ -802,6 +812,8 @@ func IsNumber(v any) bool { // IsNumeric is string/int number. should >= 0 func IsNumeric(v any) bool { + v = indirectValue(v) + if v == nil { return false } @@ -1056,6 +1068,8 @@ func Max(val, max any) bool { return valueCompare(val, max, "<=") } // Between int value in the given range. // only check for: int(X), uint(X). func Between(val any, min, max int64) bool { + val = indirectValue(val) + intVal, err := mathutil.Int64(val) if err != nil { return false diff --git a/value_test.go b/value_test.go index da73658..f9a1a28 100644 --- a/value_test.go +++ b/value_test.go @@ -49,3 +49,108 @@ func TestVal_enum(t *testing.T) { assert.Error(t, err) assert.Equal(t, "input value must be in the enum [go php]", err.Error()) } + +func TestVal_indirect(t *testing.T) { + foobar := "foobar" + err := validate.Val(&foobar, "required|string") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|starts_with:foo") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|ends_with:bar") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|contains:oba") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|eq:foobar") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|ne:foo") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|len:6") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|min_len:5") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|max_len:6") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|regex:^foo") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|ascii") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|alpha") + assert.NoError(t, err) + + err = validate.Val(&foobar, "required|alpha_num") + assert.NoError(t, err) + + foo_bar := "foo bar" + err = validate.Val(&foo_bar, "required|hasWhitespace") + assert.NoError(t, err) + + email := "hello@email.com" + err = validate.Val(&email, "required|email") + assert.NoError(t, err) + + url := "https://github.com" + err = validate.Val(&url, "required|url") + assert.NoError(t, err) + + ip := "1.1.1.1" + err = validate.Val(&ip, "required|ip") + assert.NoError(t, err) + + number := 10 + err = validate.Val(&number, "required|int|number") + assert.NoError(t, err) + + err = validate.Val(&number, "required|int_eq:10") + assert.NoError(t, err) + + err = validate.Val(&number, "required|uint") + assert.NoError(t, err) + + err = validate.Val(&number, "required|min:0") + assert.NoError(t, err) + + err = validate.Val(&number, "required|max:30") + assert.NoError(t, err) + + err = validate.Val(&number, "required|between:0,30") + assert.NoError(t, err) + + err = validate.Val(&number, "required|gt:0") + assert.NoError(t, err) + + err = validate.Val(&number, "required|lt:30") + assert.NoError(t, err) + + float := 0.1 + err = validate.Val(&float, "required|float") + assert.NoError(t, err) + + boolVal := true + err = validate.Val(&boolVal, "required|bool") + assert.NoError(t, err) + + dateVal := "2019-01-01" + err = validate.Val(&dateVal, "required|date") + assert.NoError(t, err) + + err = validate.Val(&dateVal, "required|gt_date:2006-01-02") + assert.NoError(t, err) + + err = validate.Val(&dateVal, "required|lt_date:2020-01-02") + assert.NoError(t, err) + + emptyStr := "" + err = validate.Val(&emptyStr, "required|empty") + assert.NoError(t, err) +}