in scrooge-generator/src/main/scala/com/twitter/scrooge/backend/ServiceTemplate.scala [247:443]
private def basename(fqdn: String): String = fqdn.split('.').last
def serviceDict(
service: Service,
namespace: Identifier,
includes: Seq[Include],
options: Set[ServiceOption],
genAdapt: Boolean
): Dictionary = {
val withFinagle = options.contains(WithFinagle)
val withAsClosable = options.contains(WithAsClosable)
lazy val computeInheritedFunctions: Service => (Seq[Dictionary], Seq[Dictionary]) = { service =>
{
// For service-per-endpoint, we generate a class with a value for each method, so
// method names must be unique.
val deduper = new NameDeduplicator()
val inheritedFunctions: Seq[Dictionary] =
// Note: inherited functions must be deduped first, so we walk the parent chain
// from the topmost parent down (hence the reverse).
resolvedDoc
.resolveParentServices(service, namespaceLanguage, defaultNamespace)
.reverse
.flatMap { result: ResolvedService =>
result.service.functions.map { function =>
val camelCase = function.funcName.toCamelCase
Dictionary(
"ParentServiceName" -> genID(result.serviceID),
"funcName" -> genID(camelCase),
"dedupedFuncName" -> genID(deduper.dedupe(camelCase)),
"funcObjectName" -> genID(functionObjectName(function)),
"withFuncName" -> genID(deduper.dedupe(function.funcName.toTitleCase))
)
}
}
val ownFunctions: Seq[Dictionary] = service.functions.map { function =>
functionDictionary(function, Some("Future")) ++= (
"ParentServiceName" -> v("self"),
"dedupedFuncName" -> genID(deduper.dedupe(function.funcName.toCamelCase)),
"withFuncName" -> genID(deduper.dedupe(function.funcName.toTitleCase)),
"hasValidationAnnotation" -> v(function.args.exists(hasValidationAnnotation))
)
}
(inheritedFunctions, ownFunctions)
}
}
val pkg = genID(namespace)
val pkgName = v(basename(pkg.toData))
Dictionary(
"function" -> v(templates("function")),
"package" -> pkg,
"packageName" -> pkgName,
"ServiceName" -> genID(service.sid.toTitleCase),
"docstring" -> v(service.docstring.getOrElse("")),
"syncParent" -> v(service.parent.map { p => genID(getServiceParentID(p)).append(".Iface") }),
"parent" -> v(service.parent.map { p => genQualifiedID(getServiceParentID(p), namespace) }),
"methodPerEndpointParent" -> v(service.parent.map { p =>
genQualifiedID(getServiceParentID(p), namespace).append(".MethodPerEndpoint")
}),
"futureIfaceParent" -> v(service.parent.map { p =>
genQualifiedID(getServiceParentID(p), namespace).append(".FutureIface")
}),
"genericParent" -> service.parent
.map { p => genQualifiedID(getServiceParentID(p), namespace).append(".MethodPerEndpoint") }
.getOrElse(v("_root_.com.twitter.finagle.thrift.ThriftService")),
"syncFunctions" -> v(service.functions.map { f => functionDictionary(f, None) }),
"asyncFunctions" -> v(service.functions.map { f => functionDictionary(f, Some("Future")) }),
"genericFunctions" -> v(service.functions.map { f => functionDictionary(f, Some("MM")) }),
"struct" -> v(templates("struct")),
"hasValidationAnnotation" -> v(
service.functions.exists(f => f.args.exists(hasValidationAnnotation))),
"serverValidationMixin" -> v(templates("serverValidationMixin")),
"serverValidationMethods" -> v(service.functions.map { f =>
Dictionary(
"args" -> v(f.args.map { arg =>
Dictionary(
"arg" -> genID(arg.sid),
"typeParameter" -> genFieldType(arg)
)
}),
"argsWithValidations" -> {
val validatedArg = f.args.filter(hasValidationAnnotation)
v(validatedArg.zipWithIndex.map {
case (arg, index) =>
Dictionary(
"violationArg" -> v(genID(arg.sid) + "Violations"),
"arg" -> genID(arg.sid),
"firstArg" -> v(index == 0),
"middleArgs" -> v(index > 0 && index < validatedArg.size - 1),
"lastArg" -> v(index == validatedArg.size - 1)
)
})
},
"oneArg" -> v(f.args.filter(hasValidationAnnotation).size == 1),
"typeName" -> genType(f.funcType),
"funcName" -> genID(f.funcName.toCamelCase),
"argNames" -> {
val code = f.args
.map { field => genID(field.sid).toData }
.mkString(", ")
v(code)
},
"hasValidationAnnotation" -> v(f.args.exists(hasValidationAnnotation)),
"violationReturningFuncName" -> genID(
f.funcName.toTitleCase.prepend("violationReturning")),
"fieldParams" -> genFieldParams(
f.args
) // A list of parameters with types: (a: A, b: B...)
)
}),
"thriftFunctions" -> v(service.functions.map { f =>
Dictionary(
"functionArgsStruct" ->
v(structDict(functionArgsStruct(f), Some(namespace), includes, options, genAdapt)),
"internalResultStruct" -> {
val functionResult = resultStruct(f)
v(
structDict(functionResult, Some(namespace), includes, options, genAdapt) +
Dictionary(
"successFieldType" -> getSuccessType(functionResult),
"successFieldValue" -> getSuccessValue(functionResult),
"exceptionValues" -> getExceptionFields(functionResult)
)
)
},
"annotations" -> TemplateGenerator.renderPairs(f.annotations),
"funcObjectName" -> genID(functionObjectName(f)),
"unwrapArgs" -> v(unwrapArgs(f.args.length)),
"hasValidationAnnotation" -> v(f.args.exists(hasValidationAnnotation))
) + functionDictionary(f, Some("Future"))
}),
"hasThriftFunctions" -> v(service.functions.nonEmpty),
"finagleClients" -> v(
if (withFinagle) Seq(finagleClient(service, namespace, withAsClosable)) else Seq()
),
"finagleServices" -> v(
if (withFinagle) Seq(finagleService(service, namespace)) else Seq()
),
// scalac 2.11 fails to compile classes with more than 254 method arguments
// due to https://issues.scala-lang.org/browse/SI-7324
// We skip generation of ServiceIfaces for thrift services with 255+ methods.
"generateServiceIface" -> {
val numParentFunctions = resolvedDoc
.collectParentServices(service)
.map {
case (_, service) => service.functions.length
}
.sum
val totalFunctions = service.functions.length + numParentFunctions
v(totalFunctions <= 254)
},
"withFinagle" -> v(withFinagle),
"inheritedFunctions" -> {
val (inheritedParentFunctions, ownFunctions) = computeInheritedFunctions(service)
v(ownFunctions ++ inheritedParentFunctions)
},
"inheritedParentFunctions" -> {
val (inheritedParentFunctions, _) = computeInheritedFunctions(service)
v(inheritedParentFunctions)
},
"ownFunctions" -> {
val (_, ownFunctions) = computeInheritedFunctions(service)
v(ownFunctions)
},
"annotations" -> TemplateGenerator.renderPairs(service.annotations),
"withAsClosable" -> v(withAsClosable),
"methodFilter" -> v(templates("methodFilter")),
"methodFilters" -> v(service.functions map { f =>
Dictionary(
"methodSvcNameForCompile" -> genID(f.funcName.toCamelCase),
"methodSvcNameForWire" -> v(f.originalName),
"funcObjectName" -> genID(functionObjectName(f)),
"argNames" ->
v(
f.args
.map { field => "args." + genID(field.sid).toData }
.mkString(", ")
),
"typeName" -> genType(f.funcType),
"resultNamedArg" ->
v(if (f.funcType != Void && f.funcType != OnewayVoid) "success = Some(resTry.apply())"
else ""),
"exceptions" -> v(f.throws.zipWithIndex map {
case (t, index) =>
Dictionary(
"exceptionType" -> genType(t.fieldType),
"fieldName" -> genID(t.sid),
"first" -> v(index == 0),
"last" -> v(index == f.throws.size - 1)
)
}),
"hasExceptions" -> v(f.throws.nonEmpty)
)
})
)
}