Blame vendor/github.com/deepmap/oapi-codegen/pkg/runtime/styleparam.go

Packit Service 509fd4
// Copyright 2019 DeepMap, Inc.
Packit Service 509fd4
//
Packit Service 509fd4
// Licensed under the Apache License, Version 2.0 (the "License");
Packit Service 509fd4
// you may not use this file except in compliance with the License.
Packit Service 509fd4
// You may obtain a copy of the License at
Packit Service 509fd4
//
Packit Service 509fd4
// http://www.apache.org/licenses/LICENSE-2.0
Packit Service 509fd4
//
Packit Service 509fd4
// Unless required by applicable law or agreed to in writing, software
Packit Service 509fd4
// distributed under the License is distributed on an "AS IS" BASIS,
Packit Service 509fd4
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service 509fd4
// See the License for the specific language governing permissions and
Packit Service 509fd4
// limitations under the License.
Packit Service 509fd4
package runtime
Packit Service 509fd4
Packit Service 509fd4
import (
Packit Service 509fd4
	"errors"
Packit Service 509fd4
	"fmt"
Packit Service 509fd4
	"reflect"
Packit Service 509fd4
	"sort"
Packit Service 509fd4
	"strconv"
Packit Service 509fd4
	"strings"
Packit Service 509fd4
	"time"
Packit Service 509fd4
)
Packit Service 509fd4
Packit Service 509fd4
// Given an input value, such as a primitive type, array or object, turn it
Packit Service 509fd4
// into a parameter based on style/explode definition.
Packit Service 509fd4
func StyleParam(style string, explode bool, paramName string, value interface{}) (string, error) {
Packit Service 509fd4
	t := reflect.TypeOf(value)
Packit Service 509fd4
	v := reflect.ValueOf(value)
Packit Service 509fd4
Packit Service 509fd4
	// Things may be passed in by pointer, we need to dereference, so return
Packit Service 509fd4
	// error on nil.
Packit Service 509fd4
	if t.Kind() == reflect.Ptr {
Packit Service 509fd4
		if v.IsNil() {
Packit Service 509fd4
			return "", fmt.Errorf("value is a nil pointer")
Packit Service 509fd4
		}
Packit Service 509fd4
		v = reflect.Indirect(v)
Packit Service 509fd4
		t = v.Type()
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	switch t.Kind() {
Packit Service 509fd4
	case reflect.Slice:
Packit Service 509fd4
		n := v.Len()
Packit Service 509fd4
		sliceVal := make([]interface{}, n)
Packit Service 509fd4
		for i := 0; i < n; i++ {
Packit Service 509fd4
			sliceVal[i] = v.Index(i).Interface()
Packit Service 509fd4
		}
Packit Service 509fd4
		return styleSlice(style, explode, paramName, sliceVal)
Packit Service 509fd4
	case reflect.Struct:
Packit Service 509fd4
		return styleStruct(style, explode, paramName, value)
Packit Service 509fd4
	case reflect.Map:
Packit Service 509fd4
		return styleMap(style, explode, paramName, value)
Packit Service 509fd4
	default:
Packit Service 509fd4
		return stylePrimitive(style, explode, paramName, value)
Packit Service 509fd4
	}
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func styleSlice(style string, explode bool, paramName string, values []interface{}) (string, error) {
Packit Service 509fd4
	if style == "deepObject" {
Packit Service 509fd4
		if !explode {
Packit Service 509fd4
			return "", errors.New("deepObjects must be exploded")
Packit Service 509fd4
		}
Packit Service 509fd4
		return MarshalDeepObject(values, paramName)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	var prefix string
Packit Service 509fd4
	var separator string
Packit Service 509fd4
Packit Service 509fd4
	switch style {
Packit Service 509fd4
	case "simple":
Packit Service 509fd4
		separator = ","
Packit Service 509fd4
	case "label":
Packit Service 509fd4
		prefix = "."
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = "."
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
		}
Packit Service 509fd4
	case "matrix":
Packit Service 509fd4
		prefix = fmt.Sprintf(";%s=", paramName)
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = prefix
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
		}
Packit Service 509fd4
	case "form":
Packit Service 509fd4
		prefix = fmt.Sprintf("%s=", paramName)
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = "&" + prefix
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
		}
Packit Service 509fd4
	case "spaceDelimited":
Packit Service 509fd4
		prefix = fmt.Sprintf("%s=", paramName)
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = "&" + prefix
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = " "
Packit Service 509fd4
		}
Packit Service 509fd4
	case "pipeDelimited":
Packit Service 509fd4
		prefix = fmt.Sprintf("%s=", paramName)
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = "&" + prefix
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = "|"
Packit Service 509fd4
		}
Packit Service 509fd4
	default:
Packit Service 509fd4
		return "", fmt.Errorf("unsupported style '%s'", style)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// We're going to assume here that the array is one of simple types.
Packit Service 509fd4
	var err error
Packit Service 509fd4
	parts := make([]string, len(values))
Packit Service 509fd4
	for i, v := range values {
Packit Service 509fd4
		parts[i], err = primitiveToString(v)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
	return prefix + strings.Join(parts, separator), nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func sortedKeys(strMap map[string]string) []string {
Packit Service 509fd4
	keys := make([]string, len(strMap))
Packit Service 509fd4
	i := 0
Packit Service 509fd4
	for k := range strMap {
Packit Service 509fd4
		keys[i] = k
Packit Service 509fd4
		i++
Packit Service 509fd4
	}
Packit Service 509fd4
	sort.Strings(keys)
Packit Service 509fd4
	return keys
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
Packit Service 509fd4
// This is a special case. The struct may be a time, in which case, marshal
Packit Service 509fd4
// it in RFC3339 format.
Packit Service 509fd4
func marshalTimeValue(value interface{}) (string, bool) {
Packit Service 509fd4
	if timeVal, ok := value.(*time.Time); ok {
Packit Service 509fd4
		return timeVal.Format(time.RFC3339Nano), true
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	if timeVal, ok := value.(time.Time); ok {
Packit Service 509fd4
		return timeVal.Format(time.RFC3339Nano), true
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return "", false
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func styleStruct(style string, explode bool, paramName string, value interface{}) (string, error) {
Packit Service 509fd4
	if timeVal, ok := marshalTimeValue(value); ok {
Packit Service 509fd4
		return stylePrimitive(style, explode, paramName, timeVal)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	if style == "deepObject" {
Packit Service 509fd4
		if !explode {
Packit Service 509fd4
			return "", errors.New("deepObjects must be exploded")
Packit Service 509fd4
		}
Packit Service 509fd4
		return MarshalDeepObject(value, paramName)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	// Otherwise, we need to build a dictionary of the struct's fields. Each
Packit Service 509fd4
	// field may only be a primitive value.
Packit Service 509fd4
	v := reflect.ValueOf(value)
Packit Service 509fd4
	t := reflect.TypeOf(value)
Packit Service 509fd4
	fieldDict := make(map[string]string)
Packit Service 509fd4
Packit Service 509fd4
	for i := 0; i < t.NumField(); i++ {
Packit Service 509fd4
		fieldT := t.Field(i)
Packit Service 509fd4
		// Find the json annotation on the field, and use the json specified
Packit Service 509fd4
		// name if available, otherwise, just the field name.
Packit Service 509fd4
		tag := fieldT.Tag.Get("json")
Packit Service 509fd4
		fieldName := fieldT.Name
Packit Service 509fd4
		if tag != "" {
Packit Service 509fd4
			tagParts := strings.Split(tag, ",")
Packit Service 509fd4
			name := tagParts[0]
Packit Service 509fd4
			if name != "" {
Packit Service 509fd4
				fieldName = name
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
		f := v.Field(i)
Packit Service 509fd4
Packit Service 509fd4
		// Unset optional fields will be nil pointers, skip over those.
Packit Service 509fd4
		if f.Type().Kind() == reflect.Ptr && f.IsNil() {
Packit Service 509fd4
			continue
Packit Service 509fd4
		}
Packit Service 509fd4
		str, err := primitiveToString(f.Interface())
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
Packit Service 509fd4
		}
Packit Service 509fd4
		fieldDict[fieldName] = str
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return processFieldDict(style, explode, paramName, fieldDict)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func styleMap(style string, explode bool, paramName string, value interface{}) (string, error) {
Packit Service 509fd4
	if style == "deepObject" {
Packit Service 509fd4
		if !explode {
Packit Service 509fd4
			return "", errors.New("deepObjects must be exploded")
Packit Service 509fd4
		}
Packit Service 509fd4
		return MarshalDeepObject(value, paramName)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	dict, ok := value.(map[string]interface{})
Packit Service 509fd4
	if !ok {
Packit Service 509fd4
		return "", errors.New("map not of type map[string]interface{}")
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	fieldDict := make(map[string]string)
Packit Service 509fd4
	for fieldName, value := range dict {
Packit Service 509fd4
		str, err := primitiveToString(value)
Packit Service 509fd4
		if err != nil {
Packit Service 509fd4
			return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
Packit Service 509fd4
		}
Packit Service 509fd4
		fieldDict[fieldName] = str
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return processFieldDict(style, explode, paramName, fieldDict)
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func processFieldDict(style string, explode bool, paramName string, fieldDict map[string]string) (string, error) {
Packit Service 509fd4
	var parts []string
Packit Service 509fd4
Packit Service 509fd4
	// This works for everything except deepObject. We'll handle that one
Packit Service 509fd4
	// separately.
Packit Service 509fd4
	if style != "deepObject" {
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			for _, k := range sortedKeys(fieldDict) {
Packit Service 509fd4
				v := fieldDict[k]
Packit Service 509fd4
				parts = append(parts, k+"="+v)
Packit Service 509fd4
			}
Packit Service 509fd4
		} else {
Packit Service 509fd4
			for _, k := range sortedKeys(fieldDict) {
Packit Service 509fd4
				v := fieldDict[k]
Packit Service 509fd4
				parts = append(parts, k)
Packit Service 509fd4
				parts = append(parts, v)
Packit Service 509fd4
			}
Packit Service 509fd4
		}
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	var prefix string
Packit Service 509fd4
	var separator string
Packit Service 509fd4
Packit Service 509fd4
	switch style {
Packit Service 509fd4
	case "simple":
Packit Service 509fd4
		separator = ","
Packit Service 509fd4
	case "label":
Packit Service 509fd4
		prefix = "."
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = prefix
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
		}
Packit Service 509fd4
	case "matrix":
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = ";"
Packit Service 509fd4
			prefix = ";"
Packit Service 509fd4
		} else {
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
			prefix = fmt.Sprintf(";%s=", paramName)
Packit Service 509fd4
		}
Packit Service 509fd4
	case "form":
Packit Service 509fd4
		if explode {
Packit Service 509fd4
			separator = "&"
Packit Service 509fd4
		} else {
Packit Service 509fd4
			prefix = fmt.Sprintf("%s=", paramName)
Packit Service 509fd4
			separator = ","
Packit Service 509fd4
		}
Packit Service 509fd4
	case "deepObject":
Packit Service 509fd4
		{
Packit Service 509fd4
			if !explode {
Packit Service 509fd4
				return "", fmt.Errorf("deepObject parameters must be exploded")
Packit Service 509fd4
			}
Packit Service 509fd4
			for _, k := range sortedKeys(fieldDict) {
Packit Service 509fd4
				v := fieldDict[k]
Packit Service 509fd4
				part := fmt.Sprintf("%s[%s]=%s", paramName, k, v)
Packit Service 509fd4
				parts = append(parts, part)
Packit Service 509fd4
			}
Packit Service 509fd4
			separator = "&"
Packit Service 509fd4
		}
Packit Service 509fd4
	default:
Packit Service 509fd4
		return "", fmt.Errorf("unsupported style '%s'", style)
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	return prefix + strings.Join(parts, separator), nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
func stylePrimitive(style string, explode bool, paramName string, value interface{}) (string, error) {
Packit Service 509fd4
	strVal, err := primitiveToString(value)
Packit Service 509fd4
	if err != nil {
Packit Service 509fd4
		return "", err
Packit Service 509fd4
	}
Packit Service 509fd4
Packit Service 509fd4
	var prefix string
Packit Service 509fd4
	switch style {
Packit Service 509fd4
	case "simple":
Packit Service 509fd4
	case "label":
Packit Service 509fd4
		prefix = "."
Packit Service 509fd4
	case "matrix":
Packit Service 509fd4
		prefix = fmt.Sprintf(";%s=", paramName)
Packit Service 509fd4
	case "form":
Packit Service 509fd4
		prefix = fmt.Sprintf("%s=", paramName)
Packit Service 509fd4
	default:
Packit Service 509fd4
		return "", fmt.Errorf("unsupported style '%s'", style)
Packit Service 509fd4
	}
Packit Service 509fd4
	return prefix + strVal, nil
Packit Service 509fd4
}
Packit Service 509fd4
Packit Service 509fd4
// Converts a primitive value to a string. We need to do this based on the
Packit Service 509fd4
// Kind of an interface, not the Type to work with aliased types.
Packit Service 509fd4
func primitiveToString(value interface{}) (string, error) {
Packit Service 509fd4
	var output string
Packit Service 509fd4
Packit Service 509fd4
	// Values may come in by pointer for optionals, so make sure to dereferene.
Packit Service 509fd4
	v := reflect.Indirect(reflect.ValueOf(value))
Packit Service 509fd4
	t := v.Type()
Packit Service 509fd4
	kind := t.Kind()
Packit Service 509fd4
Packit Service 509fd4
	switch kind {
Packit Service 509fd4
	case reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
Packit Service 509fd4
		output = strconv.FormatInt(v.Int(), 10)
Packit Service 509fd4
	case reflect.Float32, reflect.Float64:
Packit Service 509fd4
		output = strconv.FormatFloat(v.Float(), 'f', -1, 64)
Packit Service 509fd4
	case reflect.Bool:
Packit Service 509fd4
		if v.Bool() {
Packit Service 509fd4
			output = "true"
Packit Service 509fd4
		} else {
Packit Service 509fd4
			output = "false"
Packit Service 509fd4
		}
Packit Service 509fd4
	case reflect.String:
Packit Service 509fd4
		output = v.String()
Packit Service 509fd4
	default:
Packit Service 509fd4
		return "", fmt.Errorf("unsupported type %s", reflect.TypeOf(value).String())
Packit Service 509fd4
	}
Packit Service 509fd4
	return output, nil
Packit Service 509fd4
}