in internal/api/visibility.go [171:313]
func (vv *validateVisibility) recurse(newVal, curVal reflect.Value, mapKey, namespace, fieldname string, implicitVisibility VisibilityFlags) {
flags, ok := GetVisibilityFlags(vv.structTagMap[mapKey])
if !ok {
flags = implicitVisibility
}
if newVal.Type() != curVal.Type() {
panic(fmt.Sprintf("%s: value types differ (%s vs %s)", join(namespace, fieldname), newVal.Type().Name(), curVal.Type().Name()))
}
// Generated API structs are all pointer fields. A nil pointer in
// the incoming request (newVal) means the value is absent, which
// is always acceptable for visibility validation.
switch newVal.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
if newVal.IsNil() {
return
}
}
switch newVal.Kind() {
case reflect.Bool:
if newVal.Bool() != curVal.Bool() {
vv.checkFlags(flags, namespace, fieldname)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if newVal.Int() != curVal.Int() {
vv.checkFlags(flags, namespace, fieldname)
}
case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if newVal.Uint() != curVal.Uint() {
vv.checkFlags(flags, namespace, fieldname)
}
case reflect.Float32, reflect.Float64:
if newVal.Float() != curVal.Float() {
vv.checkFlags(flags, namespace, fieldname)
}
case reflect.Complex64, reflect.Complex128:
if newVal.Complex() != curVal.Complex() {
vv.checkFlags(flags, namespace, fieldname)
}
case reflect.String:
if flags.CaseInsensitive() {
if !strings.EqualFold(newVal.String(), curVal.String()) {
vv.checkFlags(flags, namespace, fieldname)
}
} else {
if newVal.String() != curVal.String() {
vv.checkFlags(flags, namespace, fieldname)
}
}
case reflect.Slice:
// We already know that newVal is not nil.
if curVal.IsNil() {
vv.checkFlags(flags, namespace, fieldname)
return
}
fallthrough
case reflect.Array:
if newVal.Len() != curVal.Len() {
vv.checkFlags(flags, namespace, fieldname)
} else {
for i := 0; i < min(newVal.Len(), curVal.Len()); i++ {
subscript := fmt.Sprintf("[%d]", i)
vv.recurse(newVal.Index(i), curVal.Index(i), mapKey, namespace, fieldname+subscript, flags)
}
}
case reflect.Interface, reflect.Pointer:
// We already know that newVal is not nil.
if curVal.IsNil() {
vv.checkFlags(flags, namespace, fieldname)
} else {
vv.recurse(newVal.Elem(), curVal.Elem(), mapKey, namespace, fieldname, flags)
}
case reflect.Map:
// Determine if newVal and curVal share identical keys.
var keysEqual = true
// We already know that newVal is not nil.
if curVal.IsNil() || newVal.Len() != curVal.Len() {
keysEqual = false
} else {
iter := newVal.MapRange()
for iter.Next() {
if !curVal.MapIndex(iter.Key()).IsValid() {
keysEqual = false
break
}
}
}
// Skip recursion if visibility check on the map itself fails.
if !keysEqual && !vv.checkFlags(flags, namespace, fieldname) {
return
}
// Initialize a zero value for when curVal is missing a key in newVal.
// If the map value type is a pointer, create a zero value for the type
// being pointed to.
var zeroVal reflect.Value
mapValueType := newVal.Type().Elem()
if mapValueType.Kind() == reflect.Ptr {
// This returns a pointer to the new value.
zeroVal = reflect.New(mapValueType.Elem())
} else {
// Follow the pointer to the new value.
zeroVal = reflect.New(mapValueType).Elem()
}
iter := newVal.MapRange()
for iter.Next() {
k := iter.Key()
subscript := fmt.Sprintf("[%q]", k.Interface())
if curVal.IsNil() || !curVal.MapIndex(k).IsValid() {
vv.recurse(newVal.MapIndex(k), zeroVal, mapKey, namespace, fieldname+subscript, flags)
} else {
vv.recurse(newVal.MapIndex(k), curVal.MapIndex(k), mapKey, namespace, fieldname+subscript, flags)
}
}
case reflect.Struct:
for i := 0; i < newVal.NumField(); i++ {
structField := newVal.Type().Field(i)
mapKeyNext := join(mapKey, structField.Name)
namespaceNext := join(namespace, fieldname)
fieldnameNext := GetJSONTagName(vv.structTagMap[mapKeyNext])
if fieldnameNext == "" {
fieldnameNext = structField.Name
}
vv.recurse(newVal.Field(i), curVal.Field(i), mapKeyNext, namespaceNext, fieldnameNext, flags)
}
}
}