// Copyright 2019 DeepMap, Inc.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
package runtimeimport ()// BindStyledParameter binds a parameter as described in the Path Parameters// section here to a Go object:// https://swagger.io/docs/specification/serialization/// It is a backward compatible function to clients generated with codegen// up to version v1.5.5. v1.5.6+ calls the function below.// Deprecated: BindStyledParameter is deprecated.func ( string, bool, string,string, interface{}) error {returnBindStyledParameterWithOptions(, , , , BindStyledParameterOptions{ParamLocation: ParamLocationUndefined,Explode: ,Required: true, })}// BindStyledParameterWithLocation binds a parameter as described in the Path Parameters// section here to a Go object:// https://swagger.io/docs/specification/serialization/// This is a compatibility function which is used by oapi-codegen v2.0.0 and earlier.// Deprecated: BindStyledParameterWithLocation is deprecated.func ( string, bool, string,ParamLocation, string, interface{}) error {returnBindStyledParameterWithOptions(, , , , BindStyledParameterOptions{ParamLocation: ,Explode: ,Required: true, // This emulates behavior before the required parameter was optional. })}// BindStyledParameterOptions defines optional arguments for BindStyledParameterWithOptionstypeBindStyledParameterOptionsstruct {// ParamLocation tells us where the parameter is located in the request.ParamLocationParamLocation// Whether the parameter should use exploded structureExplodebool// Whether the parameter is required in the queryRequiredbool}// BindStyledParameterWithOptions binds a parameter as described in the Path Parameters// section here to a Go object:// https://swagger.io/docs/specification/serialization/func ( string, string, string, any, BindStyledParameterOptions) error {if .Required {if == "" {returnfmt.Errorf("parameter '%s' is empty, can't bind its value", ) } }// Based on the location of the parameter, we need to unescape it properly.varerrorswitch .ParamLocation {caseParamLocationQuery, ParamLocationUndefined:// We unescape undefined parameter locations here for older generated code, // since prior to this refactoring, they always query unescaped. , = url.QueryUnescape()if != nil {returnfmt.Errorf("error unescaping query parameter '%s': %v", , ) }caseParamLocationPath: , = url.PathUnescape()if != nil {returnfmt.Errorf("error unescaping path parameter '%s': %v", , ) }default:// Headers and cookies aren't escaped. }// If the destination implements encoding.TextUnmarshaler we use it for bindingif , := .(encoding.TextUnmarshaler); {if := .UnmarshalText([]byte()); != nil {returnfmt.Errorf("error unmarshaling '%s' text as %T: %s", , , ) }returnnil }// Everything comes in by pointer, dereference it := reflect.Indirect(reflect.ValueOf())// This is the basic type of the destination object. := .Type()if .Kind() == reflect.Struct {// We've got a destination object, we'll create a JSON representation // of the input value, and let the json library deal with the unmarshaling , := splitStyledParameter(, .Explode, true, , )if != nil {return }returnbindSplitPartsToDestinationStruct(, , .Explode, ) }if .Kind() == reflect.Slice {// Chop up the parameter into parts based on its style , := splitStyledParameter(, .Explode, false, , )if != nil {returnfmt.Errorf("error splitting input '%s' into parts: %s", , ) }returnbindSplitPartsToDestinationArray(, ) }// Try to bind the remaining types as a base type.returnBindStringToObject(, )}// This is a complex set of operations, but each given parameter style can be// packed together in multiple ways, using different styles of separators, and// different packing strategies based on the explode flag. This function takes// as input any parameter format, and unpacks it to a simple list of strings// or key-values which we can then treat generically.// Why, oh why, great Swagger gods, did you have to make this so complicated?func ( string, bool, bool, string, string) ([]string, error) {switch {case"simple":// In the simple case, we always split on comma := strings.Split(, ",")return , nilcase"label":// In the label case, it's more tricky. In the no explode case, we have // /users/.3,4,5 for arrays // /users/.role,admin,firstName,Alex for objects // in the explode case, we have: // /users/.3.4.5 // /users/.role=admin.firstName=Alexif {// In the exploded case, split everything on periods. := strings.Split(, ".")// The first part should be an empty string because we have a // leading period.if [0] != "" {returnnil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", ) }return [1:], nil } else {// In the unexploded case, we strip off the leading period.if [0] != '.' {returnnil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", ) }// The rest is comma separated.returnstrings.Split([1:], ","), nil }case"matrix":if {// In the exploded case, we break everything up on semicolon := strings.Split(, ";")// The first part should always be empty string, since we started // with ;somethingif [0] != "" {returnnil, fmt.Errorf("invalid format for matrix parameter '%s', should start with ';'", ) } = [1:]// Now, if we have an object, we just have a list of x=y statements. // for a non-object, like an array, we have id=x, id=y. id=z, etc, // so we need to strip the prefix from each of them.if ! { := + "="for := range { [] = strings.TrimPrefix([], ) } }return , nil } else {// In the unexploded case, parameters will start with ;paramName= := ";" + + "="if !strings.HasPrefix(, ) {returnnil, fmt.Errorf("expected parameter '%s' to start with %s", , ) } := strings.TrimPrefix(, )returnstrings.Split(, ","), nil }case"form":var []stringif { = strings.Split(, "&")if ! { := + "="for := range { [] = strings.TrimPrefix([], ) } }return , nil } else { = strings.Split(, ",") := + "="for := range { [] = strings.TrimPrefix([], ) } }return , nil }returnnil, fmt.Errorf("unhandled parameter style: %s", )}// Given a set of values as a slice, create a slice to hold them all, and// assign to each one by one.func ( []string, interface{}) error {// Everything comes in by pointer, dereference it := reflect.Indirect(reflect.ValueOf())// This is the basic type of the destination object. := .Type()// We've got a destination array, bind each object one by one. // This generates a slice of the correct element type and length to // hold all the parts. := reflect.MakeSlice(, len(), len())for , := range { := BindStringToObject(, .Index().Addr().Interface())if != nil {returnfmt.Errorf("error setting array element: %w", ) } } .Set()returnnil}// Given a set of chopped up parameter parts, bind them to a destination// struct. The exploded parameter controls whether we send key value pairs// in the exploded case, or a sequence of values which are interpreted as// tuples.// Given the struct Id { firstName string, role string }, as in the canonical// swagger examples, in the exploded case, we would pass// ["firstName=Alex", "role=admin"], where in the non-exploded case, we would// pass "firstName", "Alex", "role", "admin"]//// We punt the hard work of binding these values to the object to the json// library. We'll turn those arrays into JSON strings, and unmarshal// into the struct.func ( string, []string, bool, interface{}) error {// We've got a destination object, we'll create a JSON representation // of the input value, and let the json library deal with the unmarshalingvar []stringif { = make([]string, len())for , := range { := strings.Split(, "=")iflen() != 2 {returnfmt.Errorf("parameter '%s' has invalid exploded format", ) } [] = "\"" + [0] + "\":\"" + [1] + "\"" } } else {iflen()%2 != 0 {returnfmt.Errorf("parameter '%s' has invalid format, property/values need to be pairs", ) } = make([]string, len()/2)for := 0; < len(); += 2 { := [] := [+1] [/2] = "\"" + + "\":\"" + + "\"" } } := "{" + strings.Join(, ",") + "}" := json.Unmarshal([]byte(), )if != nil {returnfmt.Errorf("error binding parameter %s fields: %s", , ) }returnnil}// BindQueryParameter works much like BindStyledParameter, however it takes a query argument// input array from the url package, since query arguments come through a// different path than the styled arguments. They're also exceptionally fussy.// For example, consider the exploded and unexploded form parameter examples:// (exploded) /users?role=admin&firstName=Alex// (unexploded) /users?id=role,admin,firstName,Alex//// In the first case, we can pull the "id" parameter off the context,// and unmarshal via json as an intermediate. Easy. In the second case, we// don't have the id QueryParam present, but must find "role", and "firstName".// what if there is another parameter similar to "ID" named "role"? We can't// tell them apart. This code tries to fail, but the moral of the story is that// you shouldn't pass objects via form styled query arguments, just use// the Content parameter form.func ( string, bool, bool, string,url.Values, interface{}) error {// dv = destination value. := reflect.Indirect(reflect.ValueOf())// intermediate value form which is either dv or dv dereferenced. := // inner code will bind the string's value to this interface.varinterface{}if {// 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. = } 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 .IsNil() { := .Type() := reflect.New(.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. = .Interface() } else {// If the destination isn't nil, just use that. = .Interface() }// Get rid of that extra indirect as compared to the required case, // so the code below doesn't have to care. = reflect.Indirect(reflect.ValueOf()) }// This is the basic type of the destination object. := .Type() := .Kind()switch {case"form":var []stringif {// 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. , := []varerrorswitch {casereflect.Slice:// In the slice case, we simply use the arguments provided by // http library.if ! {if {returnfmt.Errorf("query parameter '%s' is required", ) } else {// If an optional parameter is not found, we do nothing,returnnil } } = bindSplitPartsToDestinationArray(, )casereflect.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.varbool , = bindParamsToExplodedObject(, , )// If no fields were set, and there is no error, we will not fall // through to assign the destination.if ! {returnnil }default:// Primitive object case. We expect to have 1 value to // unmarshal.iflen() == 0 {if {returnfmt.Errorf("query parameter '%s' is required", ) } else {returnnil } }iflen() != 1 {returnfmt.Errorf("multiple values for single value parameter '%s'", ) }if ! {if {returnfmt.Errorf("query parameter '%s' is required", ) } else {// If an optional parameter is not found, we do nothing,returnnil } } = BindStringToObject([0], ) }if != nil {return }// If the parameter is required, and we've successfully unmarshaled // it, this assigns the new object to the pointer pointer.if ! { .Set(reflect.ValueOf()) }returnnil } else { , := []if ! {if {returnfmt.Errorf("query parameter '%s' is required", ) } else {returnnil } }iflen() != 1 {returnfmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", ) } = strings.Split([0], ",") }varerrorswitch {casereflect.Slice: = bindSplitPartsToDestinationArray(, )casereflect.Struct: = bindSplitPartsToDestinationStruct(, , , )default:iflen() == 0 {if {returnfmt.Errorf("query parameter '%s' is required", ) } else {returnnil } }iflen() != 1 {returnfmt.Errorf("multiple values for single value parameter '%s'", ) } = BindStringToObject([0], ) }if != nil {return }if ! { .Set(reflect.ValueOf()) }returnnilcase"deepObject":if ! {returnerrors.New("deepObjects must be exploded") }returnUnmarshalDeepObject(, , )case"spaceDelimited", "pipeDelimited":returnfmt.Errorf("query arguments of style '%s' aren't yet supported", )default:returnfmt.Errorf("style '%s' on parameter '%s' is invalid", , ) }}// bindParamsToExplodedObject reflects the destination structure, and pulls the value for// each settable field from the given parameters map. This is to deal with the// exploded form styled object which may occupy any number of parameter names.// We don't try to be smart here, if the field exists as a query argument,// set its value. This function returns a boolean, telling us whether there was// anything to bind. There will be nothing to bind if a parameter isn't found by name,// or none of an exploded object's fields are present.func ( string, url.Values, interface{}) (bool, error) {// Dereference pointers to their destination values , , := indirect()if != nil { , := []if ! {returnfalse, nil }returntrue, BindStringToObject(.Get(), ) }if .Kind() != reflect.Struct {returnfalse, fmt.Errorf("unmarshaling query arg '%s' into wrong type", ) } := falsefor := 0; < .NumField(); ++ { := .Field()// Skip unsettable fields, such as internal ones.if !.Field().CanSet() {continue }// Find the json annotation on the field, and use the json specified // name if available, otherwise, just the field name. := .Tag.Get("json") := .Nameif != "" { := strings.Split(, ",") := [0]if != "" { = } }// At this point, we look up field name in the parameter list. , := []if {iflen() != 1 {returnfalse, fmt.Errorf("field '%s' specified multiple times for param '%s'", , ) } := BindStringToObject([0], .Field().Addr().Interface())if != nil {returnfalse, fmt.Errorf("could not bind query arg '%s' to request object: %s'", , ) } = true } }return , nil}// indirectfunc ( interface{}) (interface{}, reflect.Value, reflect.Type) { := reflect.ValueOf()if .Type().NumMethod() > 0 && .CanInterface() {if , := .Interface().(Binder); {return , reflect.Value{}, nil } } = reflect.Indirect() := .Type()// special handling for custom types which might look like an object. We // don't want to use object binding on them, but rather treat them as // primitive types. time.Time{} is a unique case since we can't add a Binder // to it without changing the underlying generated code.if .ConvertibleTo(reflect.TypeOf(time.Time{})) {return , reflect.Value{}, nil }if .ConvertibleTo(reflect.TypeOf(types.Date{})) {return , reflect.Value{}, nil }returnnil, , }
The pages are generated with Goldsv0.7.6. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.