Factor out API defaulting from validation logic
Currently, the validation logic validates fields in an object and supply default values wherever applies. This change factors out defaulting to a set of defaulting callback functions for decoding (see #1502 for more discussion). * This change is based on pull request 2587. * Most defaulting has been migrated to defaults.go where the defaulting functions are added. * validation_test.go and converter_test.go have been adapted to not testing the default values. * Fixed all tests with that create invalid objects with the absence of defaulting logic.
This commit is contained in:
@@ -233,3 +233,140 @@ func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
||||
}
|
||||
return e.deepValueEqual(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
||||
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
if v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
return v1.String() == v2.String()
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueDerive(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueDerive(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if v1.CanInterface() && v2.CanInterface() {
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
return v1.CanInterface() == v2.CanInterface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepDerivative is similar to DeepEqual except that unset fields in a1 are
|
||||
// ignored (not compared). This allows we to focus on the fields that matter to
|
||||
// the semantic comparison.
|
||||
//
|
||||
// The unset fields include a nil pointer and an empty string.
|
||||
func (e Equalities) DeepDerivative(a1, a2 interface{}) bool {
|
||||
if a1 == nil {
|
||||
return true
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueDerive(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
Reference in New Issue
Block a user