Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
main.de.jensklingenberg.ktorfit.model.FunctionData.kt Maven / Gradle / Ivy
package de.jensklingenberg.ktorfit.model
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import de.jensklingenberg.ktorfit.model.annotations.FunctionAnnotation
import de.jensklingenberg.ktorfit.model.annotations.HttpMethod
import de.jensklingenberg.ktorfit.model.annotations.HttpMethodAnnotation
import de.jensklingenberg.ktorfit.model.annotations.Multipart
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Body
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Field
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.FieldMap
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Path
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Url
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.RequestBuilder
import de.jensklingenberg.ktorfit.utils.anyInstance
import de.jensklingenberg.ktorfit.utils.getFormUrlEncodedAnnotation
import de.jensklingenberg.ktorfit.utils.getHeaderAnnotation
import de.jensklingenberg.ktorfit.utils.getMultipartAnnotation
import de.jensklingenberg.ktorfit.utils.getStreamingAnnotation
import de.jensklingenberg.ktorfit.utils.isSuspend
import de.jensklingenberg.ktorfit.utils.parseHTTPMethodAnno
import de.jensklingenberg.ktorfit.utils.resolveTypeName
data class FunctionData(
val name: String,
val returnType: ReturnTypeData,
val isSuspend: Boolean = false,
val parameterDataList: List,
val annotations: List = emptyList(),
val httpMethodAnnotation: HttpMethodAnnotation,
)
/**
* Collect all [HttpMethodAnnotation] from a [KSFunctionDeclaration]
* @return list of [HttpMethodAnnotation]
*/
private fun getHttpMethodAnnotations(ksFunctionDeclaration: KSFunctionDeclaration): List {
val getAnno = ksFunctionDeclaration.parseHTTPMethodAnno("GET")
val putAnno = ksFunctionDeclaration.parseHTTPMethodAnno("PUT")
val postAnno = ksFunctionDeclaration.parseHTTPMethodAnno("POST")
val deleteAnno = ksFunctionDeclaration.parseHTTPMethodAnno("DELETE")
val headAnno = ksFunctionDeclaration.parseHTTPMethodAnno("HEAD")
val optionsAnno = ksFunctionDeclaration.parseHTTPMethodAnno("OPTIONS")
val patchAnno = ksFunctionDeclaration.parseHTTPMethodAnno("PATCH")
val httpAnno = ksFunctionDeclaration.parseHTTPMethodAnno("HTTP")
return listOfNotNull(getAnno, postAnno, putAnno, deleteAnno, headAnno, optionsAnno, patchAnno, httpAnno)
}
fun KSFunctionDeclaration.toFunctionData(logger: KSPLogger): FunctionData {
val funcDeclaration = this
val functionName = funcDeclaration.simpleName.asString()
val functionParameters = funcDeclaration.parameters.map { it.createParameterData(logger) }
val resolvedReturnType =
funcDeclaration.returnType?.resolve() ?: throw IllegalStateException("Return type not found")
val returnType =
ReturnTypeData(
name = resolvedReturnType.resolveTypeName(),
parameterType = resolvedReturnType,
)
val functionAnnotationList = mutableListOf()
funcDeclaration.getMultipartAnnotation()?.let {
functionAnnotationList.add(it)
}
if (funcDeclaration.typeParameters.isNotEmpty()) {
logger.error(
KtorfitError.FUNCTION_OR_PARAMETERS_TYPES_MUST_NOT_INCLUDE_ATYPE_VARIABLE_OR_WILDCARD,
funcDeclaration,
)
}
funcDeclaration.getHeaderAnnotation()?.let { headers ->
headers.value.forEach {
// Check if headers are in valid format
try {
val (key, value) = it.split(":")
} catch (exception: Exception) {
logger.error(KtorfitError.HEADERS_VALUE_MUST_BE_IN_FORM + it, funcDeclaration)
}
}
functionAnnotationList.add(headers)
}
funcDeclaration.getFormUrlEncodedAnnotation()?.let { formUrlEncoded ->
val isWithoutFieldOrFieldMap =
functionParameters.none { it.hasAnnotation() || it.hasAnnotation() }
if (isWithoutFieldOrFieldMap) {
logger.error(
KtorfitError.FORM_ENCODED_METHOD_MUST_CONTAIN_AT_LEAST_ONE_FIELD_OR_FIELD_MAP,
funcDeclaration,
)
}
if (funcDeclaration.getMultipartAnnotation() != null) {
logger.error(KtorfitError.ONLY_ONE_ENCODING_ANNOTATION_IS_ALLOWED, funcDeclaration)
}
functionAnnotationList.add(formUrlEncoded)
}
funcDeclaration.getStreamingAnnotation()?.let { streaming ->
val returnsHttpStatement = returnType.name == "HttpStatement"
if (!returnsHttpStatement) {
logger.error(
KtorfitError.FOR_STREAMING_THE_RETURN_TYPE_MUST_BE_HTTP_STATEMENT,
funcDeclaration,
)
}
functionAnnotationList.add(streaming)
}
val httpMethodAnnoList = getHttpMethodAnnotations(funcDeclaration)
if (httpMethodAnnoList.isEmpty()) {
logger.error(KtorfitError.noHttpAnnotationAt(functionName), funcDeclaration)
}
if (httpMethodAnnoList.size > 1) {
logger.error(
KtorfitError.ONLY_ONE_HTTP_METHOD_IS_ALLOWED + "Found: " +
httpMethodAnnoList.joinToString {
it.httpMethod.keyword
} + " at " + functionName,
funcDeclaration,
)
}
val firstHttpMethodAnnotation = httpMethodAnnoList.first()
val isEmptyHttpPathWithoutUrlAnnotation =
firstHttpMethodAnnotation.path.isEmpty() && functionParameters.none { it.hasAnnotation() }
if (isEmptyHttpPathWithoutUrlAnnotation) {
logger.error(
KtorfitError.missingEitherKeywordUrlOrUrlParameter(firstHttpMethodAnnotation.httpMethod.keyword),
funcDeclaration,
)
}
if (functionParameters.filter { it.hasAnnotation() }.size > 1) {
logger.error(
KtorfitError.ONLY_ONE_REQUEST_BUILDER_IS_ALLOWED + " Found: " + httpMethodAnnoList.joinToString { it.toString() } + " at " +
functionName,
funcDeclaration,
)
}
when (firstHttpMethodAnnotation.httpMethod) {
HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH -> {}
else -> {
if (functionAnnotationList.anyInstance()) {
logger.error(
KtorfitError.MULTIPART_CAN_ONLY_BE_SPECIFIED_ON_HTTPMETHODS,
funcDeclaration,
)
}
if (funcDeclaration.getFormUrlEncodedAnnotation() != null) {
logger.error(
KtorfitError.FORM_URL_ENCODED_CAN_ONLY_BE_SPECIFIED_ON_HTTP_METHODS_WITH_REQUEST_BODY,
funcDeclaration,
)
}
}
}
if (functionParameters.any { it.hasAnnotation() } && firstHttpMethodAnnotation.path.isEmpty()) {
logger.error(
KtorfitError.PATH_CAN_ONLY_BE_USED_WITH_RELATIVE_URL_ON + "@${firstHttpMethodAnnotation.httpMethod.keyword}",
funcDeclaration,
)
}
functionParameters.filter { it.hasAnnotation() }.forEach {
val pathAnnotation = it.findAnnotationOrNull()
if (!firstHttpMethodAnnotation.path.contains("{${pathAnnotation?.value ?: ""}}")) {
logger.error(
KtorfitError.missingXInRelativeUrlPath(pathAnnotation?.value.orEmpty()),
funcDeclaration,
)
}
}
if (functionParameters.any { it.hasAnnotation() }) {
if (functionParameters.filter { it.hasAnnotation() }.size > 1) {
logger.error(KtorfitError.MULTIPLE_URL_METHOD_ANNOTATIONS_FOUND, funcDeclaration)
}
if (firstHttpMethodAnnotation.path.isNotEmpty()) {
logger.error(
KtorfitError.urlCanOnlyBeUsedWithEmpty(firstHttpMethodAnnotation.httpMethod.keyword),
funcDeclaration,
)
}
}
if (functionParameters.any { it.hasAnnotation() } && funcDeclaration.getFormUrlEncodedAnnotation() == null) {
logger.error(KtorfitError.FIELD_PARAMETERS_CAN_ONLY_BE_USED_WITH_FORM_ENCODING, funcDeclaration)
}
if (functionParameters.any { it.hasAnnotation() } && funcDeclaration.getFormUrlEncodedAnnotation() == null) {
logger.error(
KtorfitError.FIELD_MAP_PARAMETERS_CAN_ONLY_BE_USED_WITH_FORM_ENCODING,
funcDeclaration,
)
}
if (functionParameters.any { it.hasAnnotation() } && funcDeclaration.getFormUrlEncodedAnnotation() != null) {
logger.error(
KtorfitError.BODY_PARAMETERS_CANNOT_BE_USED_WITH_FORM_OR_MULTI_PART_ENCODING,
funcDeclaration,
)
}
return FunctionData(
functionName,
returnType,
funcDeclaration.isSuspend,
functionParameters,
functionAnnotationList,
firstHttpMethodAnnotation,
)
}