Skip to content

Commit 3865580

Browse files
Composable codegen support
1 parent b77572f commit 3865580

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

ksp/core-processor/jvm/src/dev/programadorthi/routing/ksp/RoutingProcessor.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ private class RoutingProcessor(
4646
) : SymbolProcessor {
4747
private var invoked = false
4848

49+
private val fileName: String
50+
get() = options["Routing_Module_Name"] ?: "Module"
51+
52+
private val composableEnabled: Boolean
53+
get() = options["Routing_Compose_Enable"]?.toBooleanStrictOrNull() ?: false
54+
4955
override fun process(resolver: Resolver): List<KSAnnotated> {
5056
if (invoked) {
5157
return emptyList()
@@ -97,7 +103,13 @@ private class RoutingProcessor(
97103
"@Route with regex can't be named"
98104
}
99105

106+
val isComposable = annotations.any { it.shortName.asString() == "Composable" }
107+
100108
if (isRegexRoute) {
109+
check(!isComposable) {
110+
// TODO: Add regex support to composable handle
111+
"Combining @Route(regex = ...) and @Composable are not supported for $qualifiedName"
112+
}
101113
if (routeAnnotation.method.isBlank()) {
102114
configureSpec
103115
.beginControlFlow(
@@ -123,14 +135,18 @@ private class RoutingProcessor(
123135
routeAnnotation.name.isBlank() -> "name = null"
124136
else -> """name = "${routeAnnotation.name}""""
125137
}
138+
var memberName = handle
139+
if (composableEnabled && isComposable) {
140+
memberName = composable
141+
}
126142
if (routeAnnotation.method.isBlank()) {
127143
configureSpec
128-
.beginControlFlow("%M(path = %S, $named)", handle, routeAnnotation.path)
144+
.beginControlFlow("%M(path = %S, $named)", memberName, routeAnnotation.path)
129145
} else {
130146
val template =
131147
"""%M(path = %S, $named, method = %M(value = "${routeAnnotation.method}"))"""
132148
configureSpec
133-
.beginControlFlow(template, handle, routeAnnotation.path, routeMethod)
149+
.beginControlFlow(template, memberName, routeAnnotation.path, routeMethod)
134150
}
135151
}
136152

@@ -320,7 +336,7 @@ private class RoutingProcessor(
320336
FileSpec
321337
.builder(
322338
packageName = "dev.programadorthi.routing.generated",
323-
fileName = "${options["Routing_Module_Name"] ?: "Module"}Routes",
339+
fileName = "${fileName}Routes",
324340
)
325341
.addFileComment("Generated by Kotlin Routing")
326342
.addFunction(this)
@@ -358,6 +374,7 @@ private class RoutingProcessor(
358374
}
359375

360376
private companion object {
377+
private val composable = MemberName("dev.programadorthi.routing.compose", "composable")
361378
private val handle = MemberName("dev.programadorthi.routing.core", "handle")
362379
private val routeMethod = MemberName("dev.programadorthi.routing.core", "RouteMethod")
363380
private val call = MemberName("dev.programadorthi.routing.core.application", "call")

samples/ksp-sample/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ plugins {
44
kotlin("multiplatform")
55
alias(libs.plugins.ksp)
66
id("dev.programadorthi.routing") version "0.0.99"
7+
alias(libs.plugins.jetbrains.compose)
8+
alias(libs.plugins.compose.compiler)
9+
}
10+
11+
ksp {
12+
arg("Routing_Compose_Enable", "true")
713
}
814

915
kotlin {
@@ -18,7 +24,9 @@ kotlin {
1824
commonMain {
1925
dependencies {
2026
implementation(projects.core)
27+
implementation(projects.integration.compose)
2128
implementation(projects.ksp.coreAnnotations)
29+
implementation(compose.runtime)
2230
}
2331
}
2432
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package dev.programadorthi.routing.sample
2+
3+
import androidx.compose.runtime.Composable
4+
import dev.programadorthi.routing.annotation.Body
5+
import dev.programadorthi.routing.annotation.Path
6+
import dev.programadorthi.routing.annotation.Route
7+
import dev.programadorthi.routing.core.application.Application
8+
import io.ktor.http.Parameters
9+
import io.ktor.util.Attributes
10+
11+
@Route("/compose")
12+
@Composable
13+
fun compose() {
14+
println(">>>> I'm routing")
15+
}
16+
17+
@Route(path = "/compose/{id}")
18+
@Composable
19+
fun compose(id: Double) {
20+
println(">>>> ID: $id")
21+
}
22+
23+
@Route(path = "/composeNamed/{name}", name = "composeNamed")
24+
@Composable
25+
fun composeNamed(name: String) {
26+
println(">>>> name: $name")
27+
}
28+
29+
@Route(path = "/composeCustom/{random}", name = "composeCustom")
30+
@Composable
31+
fun composeCustom(@Path("random") value: String) {
32+
println(">>>> value: $value")
33+
}
34+
35+
@Route(path = "/composeOptional/{id?}")
36+
@Composable
37+
fun composeOptional(id: Char?) {
38+
println(">>>> Optional ID: $id")
39+
}
40+
41+
@Route(path = "/composeTailcard/{param...}")
42+
@Composable
43+
fun composeTailcard(param: List<String>?) {
44+
println(">>>> Tailcard params: $param")
45+
}
46+
47+
@Route("/compose-with-body")
48+
@Composable
49+
fun composeWithBody(@Body user: User) {
50+
println(">>>> with body $user")
51+
}
52+
53+
@Route("/compose-with-null-body")
54+
@Composable
55+
fun composeWithNullBody(@Body user: User?) {
56+
println(">>>> null body $user")
57+
}
58+
59+
@Route("/compose", method = "PUSH")
60+
@Composable
61+
fun composeByPushMethod() {
62+
println(">>>> I'm pushing a route")
63+
}
64+
65+
@Route("/compose/{part1}/{part2}")
66+
@Composable
67+
fun composeMultiParameters(part1: Int, part2: String) {
68+
println(">>>> Parts: $part1 and $part2")
69+
}
70+
71+
@Route("/compose-call/{part1}/{part2}")
72+
@Composable
73+
fun composeCallParameters(
74+
application: Application,
75+
parameters: Parameters,
76+
attributes: Attributes,
77+
) {
78+
println(
79+
"""
80+
>>>> application: $application
81+
>>>> parameters: $parameters
82+
>>>> attributes: $attributes
83+
""".trimIndent()
84+
)
85+
}

0 commit comments

Comments
 (0)