Skip to content

Commit

Permalink
Compose routing by regex
Browse files Browse the repository at this point in the history
  • Loading branch information
programadorthi committed Nov 21, 2024
1 parent d70d1de commit c29b142
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ public fun Route.composable(
body: @Composable PipelineContext<Unit, ApplicationCall>.() -> Unit,
): Route = route(path = path, name = name, method = method) { composable(body) }

@KtorDsl
public fun Route.composable(
path: Regex,
body: @Composable PipelineContext<Unit, ApplicationCall>.() -> Unit,
): Route = route(path = path) { composable(body) }

@KtorDsl
public fun Route.composable(
path: Regex,
method: RouteMethod,
body: @Composable PipelineContext<Unit, ApplicationCall>.() -> Unit,
): Route = route(path = path, method = method) { composable(body) }

@KtorDsl
public fun Route.composable(body: @Composable PipelineContext<Unit, ApplicationCall>.() -> Unit) {
val routing = asRouting ?: error("Your route $this must have a parent Routing")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fun tailcard(param: List<String>?) {
invoked += "/tailcard/{param...}" to (param ?: emptyList())
}

/*@Route(regex = ".+/hello")
@Route(regex = ".+/hello")
@Composable
fun regex1() {
invoked += ".+/hello" to listOf()
Expand All @@ -70,7 +70,7 @@ fun regex1() {
@Composable
fun regex2(number: Int) {
invoked += "/(?<number>\\d+)" to listOf(number)
}*/
}

@Route("/with-body")
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,80 @@ class ComposeRoutingTest {
// THEN
assertNull(poppedMessage, "Pop result should be cleared after other routing call")
}

@Test
fun shouldComposeByRegex() =
runComposeTest { coroutineContext, composition, clock ->
// GIVEN
val fakeContent = FakeContent()

val routing =
routing(parentCoroutineContext = coroutineContext) {
composable(path = "/initial") {
fakeContent.content = "I'm the initial content"
fakeContent.Composable()
}
composable(path = Regex("/(?<number>\\d+)")) {
fakeContent.content = "I'm the regex based content with ${call.parameters["number"]}"
fakeContent.Composable()
}
}

composition.setContent {
Routing(
routing = routing,
startUri = "/initial",
)
}

// WHEN
advanceTimeBy(99) // Ask for start uri routing
clock.sendFrame(0L) // Ask for recomposition

routing.push(path = "/123")
advanceTimeBy(99) // Ask for /path routing
clock.sendFrame(0L) // Ask for recomposition

// THEN
assertEquals("I'm the regex based content with 123", fakeContent.result)
}

@Test
fun shouldComposeByRegexWithMultipleParameters() =
runComposeTest { coroutineContext, composition, clock ->
// GIVEN
val fakeContent = FakeContent()

val routing =
routing(parentCoroutineContext = coroutineContext) {
composable(path = "/initial") {
fakeContent.content = "I'm the initial content"
fakeContent.Composable()
}
route(path = Regex("/(?<number>\\d+)")) {
composable(path = Regex("(?<user>\\w+)/(?<login>.+)")) {
fakeContent.content = "Regex with ${call.parameters}"
fakeContent.Composable()
}
}
}

composition.setContent {
Routing(
routing = routing,
startUri = "/initial",
)
}

// WHEN
advanceTimeBy(99) // Ask for start uri routing
clock.sendFrame(0L) // Ask for recomposition

routing.push(path = "/456/qwe/rty")
advanceTimeBy(99) // Ask for /path routing
clock.sendFrame(0L) // Ask for recomposition

// THEN
assertEquals("Regex with Parameters [number=[456], user=[qwe], login=[rty]]", fakeContent.result)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import io.ktor.util.Attributes
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlin.random.Random
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
Expand Down Expand Up @@ -267,7 +266,6 @@ class ComposeRoutingByAnnotationsTest {
assertEquals(listOf("p1", "p2", "p3", "p4"), invoked.remove("/tailcard/{param...}"))
}

@Ignore("Compose Regex is not supported yet")
@Test
fun shouldHandleByRegex() =
runComposeTest { coroutineContext, composition, clock ->
Expand Down Expand Up @@ -295,7 +293,6 @@ class ComposeRoutingByAnnotationsTest {
assertEquals(listOf(), invoked.remove(".+/hello"))
}

@Ignore("Compose Regex is not supported yet")
@Test
fun shouldHandleByRegexWithParameters() =
runComposeTest { coroutineContext, composition, clock ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,22 @@ private class RoutingProcessor(
}

val isScreen = classKind != null
val isComposable = !isScreen && annotations.any { it.shortName.asString() == "Composable" }
val memberName = when {
annotations.any { it.shortName.asString() == "Composable" } -> composable
isScreen -> screen
else -> handle
}

if (isRegexRoute) {
check(!isComposable && !isScreen) {
check(!isScreen) {
// TODO: Add regex support to composable handle
"$qualifiedName has @Route(regex = ...) that cannot be applied to @Composable or Voyager Screen"
}
if (routeAnnotation.method.isBlank()) {
configureSpec
.beginControlFlow(
"%M(path = %T(%S))",
handle,
memberName,
Regex::class,
routeAnnotation.regex
)
Expand All @@ -172,7 +176,7 @@ private class RoutingProcessor(
configureSpec
.beginControlFlow(
template,
handle,
memberName,
Regex::class,
routeAnnotation.regex,
routeMethod
Expand All @@ -183,11 +187,6 @@ private class RoutingProcessor(
routeAnnotation.name.isBlank() -> "name = null"
else -> """name = "${routeAnnotation.name}""""
}
val memberName = when {
isComposable -> composable
isScreen -> screen
else -> handle
}
logger.info(">>>> transforming -> name: $named and member: $memberName")
if (routeAnnotation.method.isBlank()) {
configureSpec
Expand Down

0 comments on commit c29b142

Please sign in to comment.