All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.copperleaf.ballast.navigation.internal.RouteMatcherImpl.kt Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
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 {
        data object Mismatch : PathMatchResult
        data class Match(val parsedParameters: Map>) : PathMatchResult
    }

    private sealed interface QueryMatchResult {
        data object Mismatch : QueryMatchResult
        data class Match(val parsedParameters: Map>) : QueryMatchResult
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy