From f09500fca7d3aa986ebfbea8087b4e477b5a4103 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Sun, 1 May 2022 18:44:56 +0300 Subject: [PATCH] Fix support for aliased time.Time types (#890) --- baked_in.go | 202 +++++++++++++++++++++--------------------- util.go | 2 +- validator.go | 2 +- validator_instance.go | 8 +- validator_test.go | 22 +++++ 5 files changed, 128 insertions(+), 108 deletions(-) diff --git a/baked_in.go b/baked_in.go index 7833f24b..bc638f2d 100644 --- a/baked_in.go +++ b/baked_in.go @@ -819,12 +819,7 @@ func isNeField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return true - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { t := currentField.Interface().(time.Time) fieldTime := field.Interface().(time.Time) @@ -832,6 +827,10 @@ func isNeField(fl FieldLevel) bool { return !fieldTime.Equal(t) } + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return true + } } // default reflect.String: @@ -872,18 +871,18 @@ func isLteCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return false - } + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - if fieldType == timeType { - - fieldTime := field.Interface().(time.Time) - topTime := topField.Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) + topTime := topField.Convert(timeType).Interface().(time.Time) return fieldTime.Before(topTime) || fieldTime.Equal(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -920,18 +919,18 @@ func isLtCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - fieldTime := field.Interface().(time.Time) - topTime := topField.Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) + topTime := topField.Convert(timeType).Interface().(time.Time) return fieldTime.Before(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -967,18 +966,18 @@ func isGteCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - fieldTime := field.Interface().(time.Time) - topTime := topField.Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) + topTime := topField.Convert(timeType).Interface().(time.Time) return fieldTime.After(topTime) || fieldTime.Equal(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -1014,18 +1013,18 @@ func isGtCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return false - } + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - if fieldType == timeType { - - fieldTime := field.Interface().(time.Time) - topTime := topField.Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) + topTime := topField.Convert(timeType).Interface().(time.Time) return fieldTime.After(topTime) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -1064,18 +1063,18 @@ func isNeCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return true - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - t := field.Interface().(time.Time) - fieldTime := topField.Interface().(time.Time) + t := field.Convert(timeType).Interface().(time.Time) + fieldTime := topField.Convert(timeType).Interface().(time.Time) return !fieldTime.Equal(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return true + } } // default reflect.String: @@ -1114,18 +1113,18 @@ func isEqCrossStructField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != topField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { - t := field.Interface().(time.Time) - fieldTime := topField.Interface().(time.Time) + t := field.Convert(timeType).Interface().(time.Time) + fieldTime := topField.Convert(timeType).Interface().(time.Time) return fieldTime.Equal(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } } // default reflect.String: @@ -1164,19 +1163,18 @@ func isEqField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return false - } + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { - if fieldType == timeType { - - t := currentField.Interface().(time.Time) - fieldTime := field.Interface().(time.Time) + t := currentField.Convert(timeType).Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) return fieldTime.Equal(t) } + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String: @@ -1711,18 +1709,18 @@ func isGteField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { - t := currentField.Interface().(time.Time) - fieldTime := field.Interface().(time.Time) + t := currentField.Convert(timeType).Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) return fieldTime.After(t) || fieldTime.Equal(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String @@ -1758,18 +1756,18 @@ func isGtField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { - t := currentField.Interface().(time.Time) - fieldTime := field.Interface().(time.Time) + t := currentField.Convert(timeType).Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) return fieldTime.After(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String @@ -1811,10 +1809,10 @@ func isGte(fl FieldLevel) bool { case reflect.Struct: - if field.Type() == timeType { + if field.Type().ConvertibleTo(timeType) { now := time.Now().UTC() - t := field.Interface().(time.Time) + t := field.Convert(timeType).Interface().(time.Time) return t.After(now) || t.Equal(now) } @@ -1857,9 +1855,9 @@ func isGt(fl FieldLevel) bool { return field.Float() > p case reflect.Struct: - if field.Type() == timeType { + if field.Type().ConvertibleTo(timeType) { - return field.Interface().(time.Time).After(time.Now().UTC()) + return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC()) } } @@ -1937,18 +1935,18 @@ func isLteField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return false - } + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { - if fieldType == timeType { - - t := currentField.Interface().(time.Time) - fieldTime := field.Interface().(time.Time) + t := currentField.Convert(timeType).Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) return fieldTime.Before(t) || fieldTime.Equal(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String @@ -1984,18 +1982,18 @@ func isLtField(fl FieldLevel) bool { fieldType := field.Type() - // Not Same underlying type i.e. struct and time - if fieldType != currentField.Type() { - return false - } - - if fieldType == timeType { + if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { - t := currentField.Interface().(time.Time) - fieldTime := field.Interface().(time.Time) + t := currentField.Convert(timeType).Interface().(time.Time) + fieldTime := field.Convert(timeType).Interface().(time.Time) return fieldTime.Before(t) } + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } } // default reflect.String @@ -2037,10 +2035,10 @@ func isLte(fl FieldLevel) bool { case reflect.Struct: - if field.Type() == timeType { + if field.Type().ConvertibleTo(timeType) { now := time.Now().UTC() - t := field.Interface().(time.Time) + t := field.Convert(timeType).Interface().(time.Time) return t.Before(now) || t.Equal(now) } @@ -2084,9 +2082,9 @@ func isLt(fl FieldLevel) bool { case reflect.Struct: - if field.Type() == timeType { + if field.Type().ConvertibleTo(timeType) { - return field.Interface().(time.Time).Before(time.Now().UTC()) + return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC()) } } diff --git a/util.go b/util.go index 56420f43..36da8551 100644 --- a/util.go +++ b/util.go @@ -82,7 +82,7 @@ BEGIN: fld := namespace var ns string - if typ != timeType { + if !typ.ConvertibleTo(timeType) { idx := strings.Index(namespace, namespaceSeparator) diff --git a/validator.go b/validator.go index c2e03583..80da095a 100644 --- a/validator.go +++ b/validator.go @@ -164,7 +164,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr typ = current.Type() - if typ != timeType { + if !typ.ConvertibleTo(timeType) { if ct != nil { diff --git a/validator_instance.go b/validator_instance.go index 8917d8c2..316ffb08 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -333,7 +333,7 @@ func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) { val = val.Elem() } - if val.Kind() != reflect.Struct || val.Type() == timeType { + if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) { return &InvalidValidationError{Type: reflect.TypeOf(s)} } @@ -378,7 +378,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt val = val.Elem() } - if val.Kind() != reflect.Struct || val.Type() == timeType { + if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) { return &InvalidValidationError{Type: reflect.TypeOf(s)} } @@ -426,7 +426,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields . val = val.Elem() } - if val.Kind() != reflect.Struct || val.Type() == timeType { + if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) { return &InvalidValidationError{Type: reflect.TypeOf(s)} } @@ -516,7 +516,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields .. val = val.Elem() } - if val.Kind() != reflect.Struct || val.Type() == timeType { + if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) { return &InvalidValidationError{Type: reflect.TypeOf(s)} } diff --git a/validator_test.go b/validator_test.go index b7bcec25..8abe0ce7 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5021,6 +5021,28 @@ func TestIsEqFieldValidation(t *testing.T) { Equal(t, errs, nil) } +func TestIsEqFieldValidationWithAliasTime(t *testing.T) { + var errs error + validate := New() + + type CustomTime time.Time + + type Test struct { + Start CustomTime `validate:"eqfield=End"` + End *time.Time + } + + now := time.Now().UTC() + + sv := &Test{ + Start: CustomTime(now), + End: &now, + } + + errs = validate.Struct(sv) + Equal(t, errs, nil) +} + func TestIsEqValidation(t *testing.T) { var errs error validate := New()