commonMain.com.copperleaf.ballast.navigation.internal.RouteMatcherImpl.kt Maven / Gradle / Ivy
package com.copperleaf.ballast.navigation.internal
import com.copperleaf.ballast.navigation.routing.PathSegment
import com.copperleaf.ballast.navigation.routing.QueryParameter
import com.copperleaf.ballast.navigation.routing.Route
import com.copperleaf.ballast.navigation.routing.RouteMatcher
import com.copperleaf.ballast.navigation.routing.UnmatchedDestination
internal data class RouteMatcherImpl(
override val routeFormat: String,
override val path: List,
override val query: List,
override val weight: Double,
) : RouteMatcher {
// Main matcher
// ---------------------------------------------------------------------------------------------------------------------
override fun match(
originalRoute: T,
unmatchedDestination: UnmatchedDestination,
): RouteMatcher.MatchResult {
return when (val pathMatchResult = matchPath(unmatchedDestination)) {
is PathMatchResult.Mismatch -> RouteMatcher.MatchResult.NoMatch(originalRoute)
is PathMatchResult.Match -> {
when (val queryMatchResult = matchQuery(unmatchedDestination)) {
is QueryMatchResult.Mismatch -> RouteMatcher.MatchResult.PartialMatch(
originalRoute = originalRoute,
parsedPathParameters = pathMatchResult.parsedParameters,
)
is QueryMatchResult.Match -> RouteMatcher.MatchResult.CompleteMatch(
originalRoute = originalRoute,
parsedPathParameters = pathMatchResult.parsedParameters,
parsedQueryParameters = queryMatchResult.parsedParameters,
)
}
}
}
}
// Helpers
// ---------------------------------------------------------------------------------------------------------------------
private fun matchPath(unmatchedDestination: UnmatchedDestination): PathMatchResult {
var i = 0
val pathParameters = buildMap {
path.forEach { currentPathSegment ->
val segmentMatchResult = currentPathSegment.matchInDestination(
unmatchedDestination.matchablePathSegments,
i
)
when (segmentMatchResult) {
is PathSegment.MatchResult.Mismatch -> {
// a path segment completely failed to match, exit early
return@matchPath PathMatchResult.Mismatch
}
is PathSegment.MatchResult.Match -> {
// we don't care about the value of this path segment, just continue normally
i += segmentMatchResult.numberOfMatchedSegments
}
is PathSegment.MatchResult.AddParam -> {
// the segment matched a parameter value, add it now
this[segmentMatchResult.name] = segmentMatchResult.values
i += segmentMatchResult.values.size
}
}
}
}
return if (i == (unmatchedDestination.matchablePathSegments.lastIndex + 1)) {
PathMatchResult.Match(pathParameters)
} else {
PathMatchResult.Mismatch
}
}
private fun matchQuery(unmatchedDestination: UnmatchedDestination): QueryMatchResult {
val filteredQueryParameters = filterQueryParameters(query, unmatchedDestination.matchableQueryParameters)
return if (filteredQueryParameters.mismatched.isNotEmpty()) {
// we had some query parameters in the route that did not have values matched in the destination
QueryMatchResult.Mismatch
} else if (filteredQueryParameters.unmatched.isNotEmpty()) {
// we had some query parameters provided through the destination that were not matched to those in the route
QueryMatchResult.Mismatch
} else {
QueryMatchResult.Match(filteredQueryParameters.matched)
}
}
internal fun filterQueryParameters(
registeredQueryParameters: List,
inputQueryParameters: Map>,
): QueryParameterFilterResults {
val matchedValues: MutableMap> = mutableMapOf()
val unmatchedValues: MutableMap> = inputQueryParameters.toMutableMap()
val mismatchedValues: MutableList = mutableListOf()
registeredQueryParameters.forEach { currentQueryParameter ->
val matchResult = currentQueryParameter.matchInDestination(unmatchedValues.toMap())
when (matchResult) {
is QueryParameter.MatchResult.Mismatch -> {
// a queryParameter completely failed to match, exit early
mismatchedValues += currentQueryParameter
}
is QueryParameter.MatchResult.Match -> {
// A parameter matched, but didn't need to add any values to the resulting values
unmatchedValues.remove(matchResult.name)
}
is QueryParameter.MatchResult.AddParams -> {
// A parameter matched and added its values to the map
matchResult.queryParameters.keys.forEach {
unmatchedValues.remove(it)
}
matchedValues += matchResult.queryParameters
}
}
}
return QueryParameterFilterResults(
input = inputQueryParameters,
matched = matchedValues.toMap(),
unmatched = unmatchedValues.toMap(),
mismatched = mismatchedValues.toList(),
)
}
internal data class QueryParameterFilterResults(
/**
* All input values provided to the filter
*/
val input: Map>,
/**
* The values passed through the filter
*/
val matched: Map>,
/**
* Values passed to the input that were not matched
*/
val unmatched: Map>,
/**
* Registered query parameters that did not match any values from the input
*/
val mismatched: List,
)
private sealed interface PathMatchResult {
object Mismatch : PathMatchResult
data class Match(val parsedParameters: Map>) : PathMatchResult
}
private sealed interface QueryMatchResult {
object Mismatch : QueryMatchResult
data class Match(val parsedParameters: Map>) : QueryMatchResult
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy