From b3711f965b6cd9f02791186d1e26c37e1a31047e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oliver=20R=C3=BCmpelein?= <oli_r@fg4f.de>
Date: Fri, 1 May 2020 13:49:55 +0200
Subject: [PATCH] Prepared single parameter handling.

---
 .../rcdbquery/dataMappings/Classification.kt  |  4 +-
 .../pheerai/rcdbquery/dataMappings/Order.kt   |  4 +-
 .../de/pheerai/rcdbquery/dataMappings/Page.kt |  6 +-
 .../rcdbquery/dataMappings/SearchTerm.kt      |  6 +-
 .../rcdbquery/dataMappings/SearchType.kt      |  6 +-
 .../rcdbquery/dataMappings/StartsWith.kt      |  6 +-
 .../pheerai/rcdbquery/dataMappings/Status.kt  |  4 +-
 .../pheerai/rcdbquery/dataMappings/Vendor.kt  |  4 +-
 .../rcdbquery/dataMappings/unknownParams.kt   |  6 +-
 .../de/pheerai/rcdbquery/dsl/rcdbQueryDsl.kt  | 62 +++++++++++++------
 10 files changed, 67 insertions(+), 41 deletions(-)

diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Classification.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Classification.kt
index 318ebca..f9e226e 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Classification.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Classification.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 enum class Classification(
     override val prettyName: String,
@@ -19,7 +19,7 @@ enum class Classification(
     }
 }
 
-fun MultiParamsBuilder.classification(body: ClassificationBuilder.() -> ClassificationBuilder): MultiParamsBuilder {
+fun ParamsBuilder.classification(body: ClassificationBuilder.() -> ClassificationBuilder): ParamsBuilder {
     val builder = ClassificationBuilder()
     builder.body()
     this[Classification.staticParamName] = builder.build()
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Order.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Order.kt
index bb6ed22..f8d42a3 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Order.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Order.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 @Suppress("unused")
 enum class Order(
@@ -51,7 +51,7 @@ enum class Order(
     }
 }
 
-fun MultiParamsBuilder.sortBy(body: OrderBuilder.() -> OrderBuilder): MultiParamsBuilder {
+fun ParamsBuilder.sortBy(body: OrderBuilder.() -> OrderBuilder): ParamsBuilder {
     val builder = OrderBuilder()
     builder.body()
     this[Order.staticParamName] = builder.build()
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Page.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Page.kt
index e73423e..1429845 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Page.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Page.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 class Page(override val paramValue: Int) : RcdbParamOption<Int> {
     override val fullName = "The page to show"
@@ -11,12 +11,12 @@ class Page(override val paramValue: Int) : RcdbParamOption<Int> {
     }
 }
 
-fun MultiParamsBuilder.page(page: Int) = if (Page.staticParamName !in this.keys()) {
+fun ParamsBuilder.page(page: Int) = if (Page.staticParamName !in this.keys()) {
     also { this[Page.staticParamName] = listOf(Page(page)) }
 } else {
     error(
         """Only one page must be given!
-            | Old page: ${this[Page.staticParamName]!![0].paramValue}
+            | Old page: ${this.getMulti(Page.staticParamName)!![0].paramValue}
             | New page: $page
         """.trimMargin()
     )
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchTerm.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchTerm.kt
index 48e98c6..5caecaa 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchTerm.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchTerm.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 class SearchTerm(override val paramValue: String) : RcdbParamOption<String> {
     override val prettyName = "Search Term"
@@ -11,13 +11,13 @@ class SearchTerm(override val paramValue: String) : RcdbParamOption<String> {
     }
 }
 
-fun MultiParamsBuilder.searchTerm(term: String) =
+fun ParamsBuilder.searchTerm(term: String) =
     if (SearchTerm.staticParamName !in this.keys()) {
         also { this[SearchTerm.staticParamName] = listOf(SearchTerm(term)) }
     } else {
         error(
             """Only one search term must ever be set
-            |   Old term: ${this[SearchTerm.staticParamName]!![0].paramValue}
+            |   Old term: ${this.getMulti(SearchTerm.staticParamName)!![0].paramValue}
             |   New term: $term""".trimMargin()
         )
     }
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchType.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchType.kt
index 8eb4bf1..c3de8e4 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchType.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/SearchType.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 @Suppress("unused")
 enum class SearchType(
@@ -19,13 +19,13 @@ enum class SearchType(
     }
 }
 
-fun MultiParamsBuilder.searchType(searchType: SearchType) =
+fun ParamsBuilder.searchType(searchType: SearchType) =
     if (SearchType.staticParamName !in this.keys()) {
         also { this[SearchType.staticParamName] = listOf(searchType) }
     } else {
         error(
             """Only one search type must ever be set
-            |   Old type: ${this[SearchType.staticParamName]!![0].prettyName}
+            |   Old type: ${this.getMulti(SearchType.staticParamName)!![0].prettyName}
             |   New type: ${searchType.prettyName}""".trimMargin()
         )
     }
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/StartsWith.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/StartsWith.kt
index 8995d1c..196b969 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/StartsWith.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/StartsWith.kt
@@ -2,7 +2,7 @@
 
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 class StartsWith(override val paramValue: String) : RcdbParamOption<String> {
     override val prettyName = "Starts with"
@@ -13,12 +13,12 @@ class StartsWith(override val paramValue: String) : RcdbParamOption<String> {
     }
 }
 
-fun MultiParamsBuilder.startsWith(term: String) = if (StartsWith.staticParamName !in this.keys()) {
+fun ParamsBuilder.startsWith(term: String) = if (StartsWith.staticParamName !in this.keys()) {
     also { this[StartsWith.staticParamName] = listOf(StartsWith(term)) }
 } else {
     error(
         """Only one starts with term must ever be set
-        |   Old term: ${this[StartsWith.staticParamName]!![0].paramValue}
+        |   Old term: ${this.getMulti(StartsWith.staticParamName)!![0].paramValue}
         |   New term: $term""".trimMargin()
     )
 }
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Status.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Status.kt
index 2f1dc39..e1eb85f 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Status.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Status.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 @Suppress("unused")
 enum class Status(
@@ -29,7 +29,7 @@ enum class Status(
     }
 }
 
-fun MultiParamsBuilder.status(body: StatusBuilder.() -> StatusBuilder): MultiParamsBuilder {
+fun ParamsBuilder.status(body: StatusBuilder.() -> StatusBuilder): ParamsBuilder {
     val builder = StatusBuilder()
     builder.body()
     this[Status.staticParamName] = builder.build()
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Vendor.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Vendor.kt
index ed03f94..15d6c41 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Vendor.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/Vendor.kt
@@ -1,6 +1,6 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 @Suppress("unused")
 enum class Vendor(
@@ -254,7 +254,7 @@ enum class Vendor(
     }
 }
 
-fun MultiParamsBuilder.vendors(body: VendorBuilder.() -> VendorBuilder): MultiParamsBuilder {
+fun ParamsBuilder.vendors(body: VendorBuilder.() -> VendorBuilder): ParamsBuilder {
     val builder = VendorBuilder()
     builder.body()
     this[Vendor.staticParamName] = builder.build()
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/unknownParams.kt b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/unknownParams.kt
index de2fb9a..583ae5c 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/unknownParams.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dataMappings/unknownParams.kt
@@ -1,13 +1,13 @@
 package de.pheerai.rcdbquery.dataMappings
 
-import de.pheerai.rcdbquery.dsl.MultiParamsBuilder
+import de.pheerai.rcdbquery.dsl.ParamsBuilder
 
 @Suppress("unused")
 @Deprecated(
     "The parameter for extra columns has not yet been discovered (if it even exists). Use multiple values in the `sortBy` param instead (first values take sorting precedence)",
     ReplaceWith("sortBy"), DeprecationLevel.ERROR
 )
-fun MultiParamsBuilder.extraColumns(orders: List<Order>): Nothing =
+fun ParamsBuilder.extraColumns(orders: List<Order>): Nothing =
     error("The parameter for extra columns has not yet been discovered (if it even exists). Use multiple values in the `sortBy` param instead (first values take sorting precedence)")
 
 @Suppress("unused")
@@ -15,5 +15,5 @@ fun MultiParamsBuilder.extraColumns(orders: List<Order>): Nothing =
     "The parameter for extra columns has not yet been discovered (if it even exists). Use multiple values in the `sortBy` param instead (first values take sorting precedence)",
     ReplaceWith("sortBy"), DeprecationLevel.ERROR
 )
-fun MultiParamsBuilder.extraColumns(vararg orders: Order): Nothing =
+fun ParamsBuilder.extraColumns(vararg orders: Order): Nothing =
     error("The parameter for extra columns has not yet been discovered (if it even exists). Use multiple values in the `sortBy` param instead (first values take sorting precedence)")
diff --git a/src/main/kotlin/de/pheerai/rcdbquery/dsl/rcdbQueryDsl.kt b/src/main/kotlin/de/pheerai/rcdbquery/dsl/rcdbQueryDsl.kt
index 095ee55..b74d278 100644
--- a/src/main/kotlin/de/pheerai/rcdbquery/dsl/rcdbQueryDsl.kt
+++ b/src/main/kotlin/de/pheerai/rcdbquery/dsl/rcdbQueryDsl.kt
@@ -3,6 +3,25 @@ package de.pheerai.rcdbquery.dsl
 import de.pheerai.rcdbquery.dataMappings.RcdbParamOption
 import de.pheerai.rcdbquery.dataMappings.SearchType
 
+data class RcdbParams(
+    val multiParams: MultiParams,
+    val singleParams: SingleParams
+) {
+    fun toStrings(): List<String> {
+        val multiParamSequence: Sequence<Pair<String, String>> = this.multiParams
+            .params
+            .asSequence()
+            .map { e -> e.key to e.value.joinToString(separator = ",") }
+        val singleParamSequence: Sequence<Pair<String,String>> = this.singleParams
+            .params
+            .asSequence()
+            .map { it.toPair() }
+        return singleParamSequence.plus(multiParamSequence)
+            .map { p -> "${p.first}=${p.second}" }
+            .toList()
+    }
+}
+
 // In preparation for new handler
 sealed class Params<T> {
     abstract val params: Map<String, T>
@@ -11,44 +30,51 @@ sealed class Params<T> {
 class MultiParams(override val params: Map<String, List<String>>) : Params<List<String>>()
 class SingleParams(override val params: Map<String, String>) : Params<String>()
 
-interface ParamsBuilder<out T, in U> {
-    fun build(): T
-    operator fun set(paramName: String, options: U)
-}
-
-class MultiParamsBuilder : ParamsBuilder<MultiParams, List<RcdbParamOption<Any>>> {
+class ParamsBuilder {
     private val multiParams: MutableMap<String, List<RcdbParamOption<Any>>> = mutableMapOf()
+    private val singleParams: MutableMap<String, RcdbParamOption<Any>> = mutableMapOf()
+
+    fun build() = RcdbParams(buildMulti(), buildSingle())
 
     /**
      * Creates the Query Params from this builder.
      * If no Search type has been set, this will default to "Coaster"
      */
-    override fun build() = multiParams.apply {
+    private fun buildMulti() = multiParams.apply {
             putIfAbsent(SearchType.staticParamName, listOf(SearchType.COASTER))
         }
         .filter { it.value.isNotEmpty() }
         .mapValues { e -> e.value.map { o -> o.paramValue.toString() } }
         .let { MultiParams(it) }
 
+    private fun buildSingle() = singleParams.apply {
+            // Defaults go here
+        }
+        .mapValues { it.toString() }
+        .let { SingleParams(it) }
 
-    override operator fun set(paramName: String, options: List<RcdbParamOption<Any>>) {
+    operator fun set(paramName: String, options: List<RcdbParamOption<Any>>) {
         this.multiParams[paramName] = options
     }
 
-    operator fun get(paramName: String) = this.multiParams[paramName]
+    operator fun set(paramName: String, option: RcdbParamOption<Any>) {
+        this.singleParams[paramName] = option
+    }
 
-    fun keys() = this.multiParams.keys.toSet()
+    fun getSingle(paramName: String) = this.multiParams[paramName] ?: this.singleParams[paramName]
+    fun getMulti(paramName: String) = this.multiParams[paramName]
+
+    fun keys() = this.multiKeys().plus(singleKeys())
+    private fun multiKeys() = this.multiParams.keys.toSet()
+    private fun singleKeys() = this.singleParams.keys.toSet()
 }
 
-fun rcdbQuery(body: MultiParamsBuilder.() -> MultiParamsBuilder) =
+fun rcdbQuery(body: ParamsBuilder.() -> ParamsBuilder) =
     RcdbUrlQuery("https://www.rcdb.com/r.htm?", rcdbQueryParams(body)).toString()
 
-data class RcdbUrlQuery(val baseUrl: String, val params: MultiParams) {
+data class RcdbUrlQuery(val baseUrl: String, val params: RcdbParams) {
     override fun toString(): String {
-        return params.params
-            .asSequence()
-            .map { e -> e.key to e.value.joinToString(separator = ",") }
-            .map { p -> "${p.first}=${p.second}" }
+        return params.toStrings()
             .joinToString(prefix = baseUrl, separator = "&")
     }
 }
@@ -56,8 +82,8 @@ data class RcdbUrlQuery(val baseUrl: String, val params: MultiParams) {
 /**
  * Builder for the parameters only (mainly because it might be possilbe in the future to have an API with a POST search object?
  */
-fun rcdbQueryParams(body: MultiParamsBuilder.() -> MultiParamsBuilder): MultiParams {
-    val builder = MultiParamsBuilder()
+fun rcdbQueryParams(body: ParamsBuilder.() -> ParamsBuilder): RcdbParams {
+    val builder = ParamsBuilder()
     builder.body()
     return builder.build()
 }