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

commonMain.com.copperleaf.ballast.navigation.routing.QueryParameter.kt Maven / Gradle / Ivy

There is a newer version: 4.2.1
Show newest version
package com.copperleaf.ballast.navigation.routing

/**
 * The types of Query Parameters which may be parsed from a Route URL format.
 */
public sealed interface QueryParameter {

    public val paramName: String?
    public val weight: Int
    public val mustBeAtEnd: Boolean
    public val isStatic: Boolean

    public fun matchInDestination(
        queryParameters: Map>,
    ): MatchResult

    public fun directions(
        queryParameters: Map>,
    ): Directions

    /**
     * /route?one=two         // require a single value at this name
     * /route?one=[two,three] // require an exact list of values at this name
     */
    public data class Static(val name: String, val values: List) : QueryParameter {
        override val paramName: String = name
        override val weight: Int = 5
        override val mustBeAtEnd: Boolean = false
        override val isStatic: Boolean = true

        override fun matchInDestination(queryParameters: Map>): MatchResult {
            val matchingStaticParameter = queryParameters[name]?.sorted()
            return if (matchingStaticParameter == values) {
                MatchResult.Match(name)
            } else {
                MatchResult.Mismatch
            }
        }

        override fun directions(queryParameters: Map>): Directions {
            return Directions(
                toAppend = mapOf(name to values),
                consumedParameterNames = emptySet()
            )
        }
    }

    /**
     * /route?one={!}  // require exactly 1 value
     * /route?one={[!]} // require 1 or more values
     * /route?one={?}  // allow 0 or 1 values
     * /route?one={[?]]} // allow 0 or more values
     */
    public data class Parameter(val name: String, val optional: Boolean, val allowMultiple: Boolean) : QueryParameter {
        override val paramName: String = name
        override val weight: Int = if (optional) 2 else 4
        override val mustBeAtEnd: Boolean = false
        override val isStatic: Boolean = optional

        override fun matchInDestination(queryParameters: Map>): MatchResult {
            val matchingParameter = queryParameters[name]
            return if (matchingParameter != null) {
                // we have a matching value
                if (matchingParameter.size > 1) {
                    if (allowMultiple) {
                        // we have multiple parameters for this name, but we are allowing multiples
                        MatchResult.AddParams(mapOf(name to matchingParameter))
                    } else {
                        // we have multiple parameters for this name, when we require at most 1. This is a mismatch
                        MatchResult.Mismatch
                    }
                } else {
                    // we have a single matching value
                    MatchResult.AddParams(mapOf(name to matchingParameter))
                }
            } else {
                // No parameters at this value were given
                if (optional) {
                    // but it's optional, so consider it a match, still
                    MatchResult.Match(name)
                } else {
                    // and we require a value, so this is a mismatch
                    MatchResult.Mismatch
                }
            }
        }

        override fun directions(queryParameters: Map>): Directions {
            val parameterValue = queryParameters[name]

            return when {
                parameterValue == null -> {
                    if (optional) {
                        Directions(
                            toAppend = emptyMap(),
                            consumedParameterNames = emptySet(),
                        )
                    } else {
                        Directions(
                            consumedParameterNames = setOf(name),
                            error = "Non-optional query parameter '$name' must be provided"
                        )
                    }
                }

                parameterValue.isEmpty() -> {
                    if (optional) {
                        Directions(
                            toAppend = emptyMap(),
                            consumedParameterNames = setOf(name),
                        )
                    } else {
                        Directions(
                            consumedParameterNames = setOf(name),
                            error = "Non-optional query parameter '$name' must be provided"
                        )
                    }
                }

                parameterValue.size == 1 -> {
                    Directions(
                        toAppend = mapOf(name to listOf(parameterValue.single())),
                        consumedParameterNames = setOf(name),
                    )
                }

                else -> {
                    if (this.allowMultiple) {
                        Directions(
                            toAppend = mapOf(name to parameterValue),
                            consumedParameterNames = setOf(name),
                        )
                    } else {
                        Directions(
                            consumedParameterNames = setOf(name),
                            error = "Query parameter '$name' can only have a single value"
                        )
                    }
                }
            }
        }
    }

    /**
     * /route?{...}
     */
    public object Remainder : QueryParameter {
        override val paramName: String? = null
        override val weight: Int = 1
        override val mustBeAtEnd: Boolean = true
        override val isStatic: Boolean = true

        override fun matchInDestination(queryParameters: Map>): MatchResult {
            // just return everything that's remaining
            return MatchResult.AddParams(queryParameters)
        }

        override fun directions(queryParameters: Map>): Directions {
            return Directions(
                toAppend = queryParameters,
                consumedParameterNames = queryParameters.keys,
            )
        }
    }

    public sealed interface MatchResult {
        public object Mismatch : MatchResult
        public data class Match(val name: String) : MatchResult
        public data class AddParams(val queryParameters: Map>) : MatchResult
    }

    public data class Directions(
        val toAppend: Map> = emptyMap(),
        val consumedParameterNames: Set = emptySet(),
        val error: String? = null,
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy