in codegen/method.go [1346:1552]
func (ms *MethodSpec) setParseQueryParamStatements(
funcSpec *compile.FunctionSpec, packageHelper *PackageHelper, hasNoBody bool,
) error {
// If a thrift field has a http.ref annotation then we
// should not read this field from query parameters.
var statements LineBuilder
var finalError error
var stack = []string{}
seenIdents := map[string]int{}
visitor := func(
goPrefix string, thriftPrefix string, field *compile.FieldSpec,
) bool {
realType := compile.RootTypeSpec(field.Type)
longFieldName := goPrefix + "." + PascalCase(field.Name)
longQueryName, shortQueryParam := ms.getQueryParamInfo(field, thriftPrefix)
// Skip if there are no query params in the field or its components
if !ms.hasQueryParams(field, hasNoBody) {
return false
}
if len(stack) > 0 {
if !strings.HasPrefix(longFieldName, stack[len(stack)-1]) {
stack = stack[:len(stack)-1]
statements.append("}")
}
}
customType, err := getCustomType(packageHelper, field.Type)
if err != nil {
finalError = err
return true
}
var isList, isSet bool
var customElemType string
var isEnumElem bool
switch t := realType.(type) {
// Before you ask -- yes duplicated code because ValueSpec is not defined in the generic interface
case *compile.ListSpec:
isList = true
customElemType, err = getCustomType(packageHelper, t.ValueSpec)
if err != nil {
finalError = err
return true
}
_, isEnumElem = t.ValueSpec.(*compile.EnumSpec)
case *compile.SetSpec:
isSet = true
customElemType, err = getCustomType(packageHelper, t.ValueSpec)
if err != nil {
finalError = err
return true
}
_, isEnumElem = t.ValueSpec.(*compile.EnumSpec)
case *compile.StructSpec:
typeName, err := GoType(packageHelper, realType)
if err != nil {
finalError = err
return true
}
if !field.Required {
stack = append(stack, longFieldName)
applicableQueryParams := ms.getContainedQueryParams(field, hasNoBody, "")
statements.append("var _queryNeeded bool")
statements.appendf("for _, _pfx := range %#v {", applicableQueryParams)
statements.append("if _queryNeeded = req.HasQueryPrefix(_pfx); _queryNeeded {")
statements.append("break")
statements.append("}")
statements.append("}")
statements.append("if _queryNeeded {")
}
statements.appendf("if requestBody%s == nil {", longFieldName)
statements.appendf("requestBody%s = &%s{}", longFieldName, typeName)
statements.append("}")
return false
}
isAggregate := isList || isSet // we do not support maps
// For disambiguation of similar names
baseIdent := makeUniqIdent(CamelCase(longQueryName), seenIdents)
identifierName := baseIdent + "Query"
okIdentifierName := baseIdent + "Ok"
// make sure value is present
if field.Required {
statements.appendf("%s := req.CheckQueryValue(%q)", okIdentifierName, shortQueryParam)
statements.appendf("if !%s {", okIdentifierName)
statements.append("return ctx")
statements.append("}")
} else {
statements.appendf("%s := req.HasQueryValue(%q)", okIdentifierName, shortQueryParam)
statements.appendf("if %s {", okIdentifierName)
}
queryRValue := fmt.Sprintf("req.%s(%q)", getQueryMethodForType(realType), shortQueryParam)
// Transform if enum
if _, isEnumType := field.Type.(*compile.EnumSpec); isEnumType {
statements.appendf("var %s %s", identifierName, customType)
tmpVar := "_tmp" + identifierName
statements.appendf("%s, ok := %s", tmpVar, queryRValue)
statements.append("if ok {")
statements.appendf("if err := %s.UnmarshalText([]byte(%s)); err != nil {",
identifierName, tmpVar)
statements.appendf("req.LogAndSendQueryError(err, %q, %q, %s)",
"enum", shortQueryParam, tmpVar)
statements.append("ok = false")
statements.append("}")
statements.append("}")
} else {
statements.appendf("%s, ok := %s", identifierName, queryRValue)
}
statements.append("if !ok {")
statements.append("return ctx")
statements.append("}")
target := identifierName
// If field is an "aggregate" with custom element types, we need to convert them first
// Note that enums and typedefs are what get in here
if customElemType != "" {
target += "Final"
valVar := "v"
if isList {
statements.appendf(
"%s := make([]%s, len(%s))",
target, customElemType, identifierName,
)
statements.appendf("for i, %s := range %s {", valVar, identifierName)
if isEnumElem {
tmpVar := "_tmp" + valVar
statements.appendf("var %s %s", tmpVar, customElemType)
statements.appendf("if err := %s.UnmarshalText([]byte(%s)); err != nil {",
tmpVar, valVar)
statements.appendf("req.LogAndSendQueryError(err, %q, %q, %s)",
"enum", shortQueryParam, valVar)
statements.append("return ctx")
statements.append("}")
valVar = tmpVar
}
statements.appendf("%s[i] = %s(%s)", target, customElemType, valVar)
statements.append("}")
} else if isSet {
statements.appendf(
"%s := make(map[%s]struct{}, len(%s))",
target, customElemType, identifierName,
)
statements.appendf("for %s := range %s {", valVar, identifierName)
if isEnumElem {
tmpVar := "_tmp" + valVar
statements.appendf("var %s %s", tmpVar, customElemType)
statements.appendf("if err := %s.UnmarshalText([]byte(%s)); err != nil {",
tmpVar, valVar)
statements.appendf("req.LogAndSendQueryError(err, %q, %q, %s)",
"enum", shortQueryParam, valVar)
statements.append("return ctx")
statements.append("}")
valVar = tmpVar
}
statements.appendf("%s[%s(%s)] = struct{}{}", target, customElemType, valVar)
statements.append("}")
}
}
var deref string
if !field.Required && !isAggregate {
deref = "*"
targetName := identifierName
if customType != "" {
targetName = fmt.Sprintf("%s(%s)", strings.ToLower(pointerMethodType(realType)), targetName)
}
target = fmt.Sprintf("ptr.%s(%s)", pointerMethodType(realType), targetName)
}
if customType != "" {
target = fmt.Sprintf("(%s%s)(%s)", deref, customType, target)
}
statements.appendf("requestBody%s = %s", longFieldName, target)
if !field.Required {
statements.append("}")
}
// new line after block.
statements.append("")
return false
}
walkFieldGroups(compile.FieldGroup(funcSpec.ArgsSpec), visitor)
for i := 0; i < len(stack); i++ {
statements.append("}")
}
if finalError != nil {
return finalError
}
ms.ParseQueryParamGoStatements = statements.GetLines()
return nil
}