11package dev.programadorthi.routing.ksp
22
3+ import com.google.devtools.ksp.getVisibility
4+ import com.google.devtools.ksp.isLocal
35import com.google.devtools.ksp.processing.CodeGenerator
46import com.google.devtools.ksp.processing.Dependencies
57import com.google.devtools.ksp.processing.KSPLogger
68import com.google.devtools.ksp.processing.Resolver
79import com.google.devtools.ksp.processing.SymbolProcessor
810import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
911import com.google.devtools.ksp.processing.SymbolProcessorProvider
12+ import com.google.devtools.ksp.symbol.FunctionKind
1013import com.google.devtools.ksp.symbol.KSAnnotated
1114import com.google.devtools.ksp.symbol.KSAnnotation
1215import com.google.devtools.ksp.symbol.KSFunctionDeclaration
16+ import com.google.devtools.ksp.symbol.KSTypeReference
17+ import com.google.devtools.ksp.symbol.Visibility
1318import com.squareup.kotlinpoet.FileSpec
1419import com.squareup.kotlinpoet.FunSpec
1520import com.squareup.kotlinpoet.KModifier
21+ import com.squareup.kotlinpoet.MemberName
1622import com.squareup.kotlinpoet.ksp.writeTo
1723import dev.programadorthi.routing.annotation.Route
1824
@@ -37,6 +43,9 @@ private class RoutingProcessor(
3743 }
3844 invoked = true
3945
46+ val call = MemberName (" dev.programadorthi.routing.core.application" , " call" )
47+ val handle = MemberName (" dev.programadorthi.routing.core" , " handle" )
48+
4049 val configureSpec = FunSpec
4150 .builder(" configure" )
4251 .addModifiers(KModifier .INTERNAL )
@@ -46,21 +55,56 @@ private class RoutingProcessor(
4655 .getSymbolsWithAnnotation(Route ::class .java.name)
4756 .filterIsInstance<KSFunctionDeclaration >()
4857 .forEach { func ->
58+ val qualifiedName = func.qualifiedName?.asString()
59+
60+ check(func.functionKind == FunctionKind .TOP_LEVEL ) {
61+ " $qualifiedName fun must be a top level fun"
62+ }
63+
64+ check(func.getVisibility() != Visibility .PRIVATE ) {
65+ " $qualifiedName fun must not be private"
66+ }
67+
4968 check(func.packageName.asString().isNotBlank()) {
50- " Local functions are not supported. " +
51- " Please provide a package to fun '${func.qualifiedName?.asString()} '"
69+ " Top level fun '$qualifiedName ' must have a package"
5270 }
5371
5472 val annotation = func.getAnnotationByName(" Route" )
55- val path = annotation?.getArgumentValueByName<String >(" value" )
56-
57- configureSpec.addCode(
58- """
59- |handle(path = "$path ") {
60- | ${func.qualifiedName?.asString()} ()
61- |}
62- |""" .trimMargin()
63- )
73+ val path = checkNotNull(annotation?.getArgumentValueByName<String >(" value" )) {
74+ " No path provided to @Route annotation at '$qualifiedName '"
75+ }
76+
77+ val parameters = mutableListOf<String >()
78+
79+ for (param in func.parameters) {
80+ val paramName = param.name?.asString()
81+ check(path.contains(" {$paramName }" )) {
82+ " '$qualifiedName ' has parameter '$paramName ' that is not declared as path parameter {$paramName }"
83+ }
84+
85+ val parsed = """ $paramName = %M.parameters["$paramName "]!!"""
86+ parameters + = when (param.type.resolve()) {
87+ resolver.builtIns.booleanType -> " $parsed .toBoolean()"
88+ resolver.builtIns.byteType -> " $parsed .toByte()"
89+ resolver.builtIns.charType -> " $parsed [0]"
90+ resolver.builtIns.doubleType -> " $parsed .toDouble()"
91+ resolver.builtIns.floatType -> " $parsed .toFloat()"
92+ resolver.builtIns.intType -> " $parsed .toInt()"
93+ resolver.builtIns.longType -> " $parsed .toInt()"
94+ resolver.builtIns.shortType -> " $parsed .toInt()"
95+ resolver.builtIns.stringType -> parsed
96+ else -> error(" Path parameters must be primitive type only" )
97+ }
98+ }
99+
100+ val calls = Array (size = parameters.size) { call }
101+ val params = parameters
102+ .joinToString(prefix = " (" , postfix = " )" ) { " \n $it " }
103+
104+ configureSpec
105+ .beginControlFlow(""" %M(path = "$path ")""" , handle)
106+ .addStatement(""" $qualifiedName$params """ , * calls)
107+ .endControlFlow()
64108 }
65109
66110 FileSpec
@@ -69,7 +113,6 @@ private class RoutingProcessor(
69113 fileName = " ModuleRoutes"
70114 )
71115 .addFileComment(" Generated by Kotlin Routing" )
72- .addImport(" dev.programadorthi.routing.core" , " handle" )
73116 .addFunction(configureSpec.build())
74117 .build()
75118 .writeTo(
0 commit comments