diff --git a/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/Wisp.kt b/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/Wisp.kt index ca147c8..3369b68 100644 --- a/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/Wisp.kt +++ b/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/Wisp.kt @@ -24,16 +24,30 @@ class Wisp( */ fun resolveRoutes(uri: Uri): List { val paths = parser.parse(uri) + val queryParams = getQueryParams(uri) + return paths.map { path -> - matchAndCreate(path) ?: throw WispError.UnknownPath(path) + matchAndCreate(path, queryParams) ?: throw WispError.UnknownPath(path) + } + } + + private fun getQueryParams(uri: Uri): Map { + val params = mutableMapOf() + uri.queryParameterNames.forEach { key -> + uri.getQueryParameter(key)?.let { value -> + params[key] = value + } } + return params } - private fun matchAndCreate(path: String): Any? { + private fun matchAndCreate(path: String, queryParams: Map): Any? { for ((pattern, factory) in mergedRoutes) { - val params = WispUriMatcher.match(path, pattern) - if (params != null) { - return factory.create(params) + val pathVariables = WispUriMatcher.match(path, pattern) + if (pathVariables != null) { + // Path Variable과 Query Parameter 병합 (Query Param 우선순위 낮음) + val combinedParams = queryParams + pathVariables + return factory.create(combinedParams) } } return null @@ -71,7 +85,9 @@ class Wisp( @JvmStatic @Synchronized - fun initialize() { + fun initialize( + parser: WispUriParser = DefaultWispUriParser() + ) { if (instance == null) { val aggregatedRoutes = mutableMapOf() val loader = ServiceLoader.load(WispModuleRegistry::class.java) @@ -80,7 +96,7 @@ class Wisp( aggregatedRoutes.putAll(registry.getRoutes()) } - instance = Wisp(aggregatedRoutes) + instance = Wisp(aggregatedRoutes, parser) } } diff --git a/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/parser/DefaultWispUriParser.kt b/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/parser/DefaultWispUriParser.kt index 0bf677b..0f25dae 100644 --- a/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/parser/DefaultWispUriParser.kt +++ b/wisp-runtime/src/main/java/com/angrypodo/wisp/runtime/parser/DefaultWispUriParser.kt @@ -1,26 +1,28 @@ package com.angrypodo.wisp.runtime.parser import android.net.Uri -import com.angrypodo.wisp.runtime.WispError -import java.net.URLDecoder -import java.nio.charset.StandardCharsets -private const val STACK = "stack" +/** + * 기본 URI 파서 구현체입니다. + * URI의 경로(Path)를 지정된 구분자([delimiter])로 분리하여 라우트 경로 리스트를 생성합니다. + * + * 예: "myapp://home/product" (delimiter="/") -> ["home", "product"] + */ +class DefaultWispUriParser( + private val delimiter: String = "/" +) : WispUriParser { -class DefaultWispUriParser : WispUriParser { override fun parse(uri: Uri): List { - val encodedStack = uri.getQueryParameter(STACK) + val path = uri.path ?: return emptyList() - if (encodedStack.isNullOrBlank()) { - throw WispError.ParsingFailed(uri.toString(), "Missing 'stack' query parameter") + // 경로가 구분자로 시작하면 제거 (예: "/home" -> "home") + val trimmedPath = if (path.startsWith(delimiter)) { + path.substring(delimiter.length) + } else { + path } - return try { - val decodedStack = URLDecoder.decode(encodedStack, StandardCharsets.UTF_8.name()) - - decodedStack.split("|").filter { it.isNotBlank() } - } catch (e: Exception) { - throw WispError.ParsingFailed(uri.toString(), e.message ?: "Unknown decoding error") - } + return trimmedPath.split(delimiter) + .filter { it.isNotEmpty() } } }