func()

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)
		}
	}
}