func BindQueryParameter()

in cluster-autoscaler/cloudprovider/exoscale/internal/github.com/exoscale/egoscale/internal/runtime/bindparam.go [258:406]


func BindQueryParameter(style string, explode bool, required bool, paramName string,
	queryParams url.Values, dest interface{}) error {

	// dv = destination value.
	dv := reflect.Indirect(reflect.ValueOf(dest))

	// intermediate value form which is either dv or dv dereferenced.
	v := dv

	// inner code will bind the string's value to this interface.
	var output interface{}

	if required {
		// If the parameter is required, then the generated code will pass us
		// a pointer to it: &int, &object, and so forth. We can directly set
		// them.
		output = dest
	} else {
		// For optional parameters, we have an extra indirect. An optional
		// parameter of type "int" will be *int on the struct. We pass that
		// in by pointer, and have **int.

		// If the destination, is a nil pointer, we need to allocate it.
		if v.IsNil() {
			t := v.Type()
			newValue := reflect.New(t.Elem())
			// for now, hang onto the output buffer separately from destination,
			// as we don't want to write anything to destination until we can
			// unmarshal successfully, and check whether a field is required.
			output = newValue.Interface()
		} else {
			// If the destination isn't nil, just use that.
			output = v.Interface()
		}

		// Get rid of that extra indirect as compared to the required case,
		// so the code below doesn't have to care.
		v = reflect.Indirect(reflect.ValueOf(output))
	}

	// This is the basic type of the destination object.
	t := v.Type()
	k := t.Kind()

	switch style {
	case "form":
		var parts []string
		if explode {
			// ok, the explode case in query arguments is very, very annoying,
			// because an exploded object, such as /users?role=admin&firstName=Alex
			// isn't actually present in the parameter array. We have to do
			// different things based on destination type.
			values, found := queryParams[paramName]
			var err error

			switch k {
			case reflect.Slice:
				// In the slice case, we simply use the arguments provided by
				// http library.
				if !found {
					if required {
						return fmt.Errorf("query parameter '%s' is required", paramName)
					} else {
						return nil
					}
				}
				err = bindSplitPartsToDestinationArray(values, output)
			case reflect.Struct:
				// This case is really annoying, and error prone, but the
				// form style object binding doesn't tell us which arguments
				// in the query string correspond to the object's fields. We'll
				// try to bind field by field.
				err = bindParamsToExplodedObject(paramName, queryParams, output)
			default:
				// Primitive object case. We expect to have 1 value to
				// unmarshal.
				if len(values) == 0 {
					if required {
						return fmt.Errorf("query parameter '%s' is required", paramName)
					} else {
						return nil
					}
				}
				if len(values) != 1 {
					return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
				}
				err = BindStringToObject(values[0], output)
			}
			if err != nil {
				return err
			}
			// If the parameter is required, and we've successfully unmarshaled
			// it, this assigns the new object to the pointer pointer.
			if !required {
				dv.Set(reflect.ValueOf(output))
			}
			return nil
		} else {
			values, found := queryParams[paramName]
			if !found {
				if required {
					return fmt.Errorf("query parameter '%s' is required", paramName)
				} else {
					return nil
				}
			}
			if len(values) != 1 {
				return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", paramName)
			}
			parts = strings.Split(values[0], ",")
		}
		var err error
		switch k {
		case reflect.Slice:
			err = bindSplitPartsToDestinationArray(parts, output)
		case reflect.Struct:
			err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output)
		default:
			if len(parts) == 0 {
				if required {
					return fmt.Errorf("query parameter '%s' is required", paramName)
				} else {
					return nil
				}
			}
			if len(parts) != 1 {
				return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
			}
			err = BindStringToObject(parts[0], output)
		}
		if err != nil {
			return err
		}
		if !required {
			dv.Set(reflect.ValueOf(output))
		}
		return nil
	case "deepObject":
		if !explode {
			return errors.New("deepObjects must be exploded")
		}
		return UnmarshalDeepObject(dest, paramName, queryParams)
	case "spaceDelimited", "pipeDelimited":
		return fmt.Errorf("query arguments of style '%s' aren't yet supported", style)
	default:
		return fmt.Errorf("style '%s' on parameter '%s' is invalid", style, paramName)

	}
}