Router
package org.toilelibre.libe | |
import java.lang.reflect.Method | |
import kotlin.math.pow | |
import kotlin.reflect.KFunction | |
import kotlin.reflect.full.companionObject | |
import kotlin.reflect.full.companionObjectInstance | |
import kotlin.reflect.full.declaredFunctions | |
import kotlin.reflect.full.memberProperties | |
open class Router constructor(tableInput: String) { | |
val machineReadable = tableInput | |
.trimIndent() | |
.let { | |
"╔╦╗╠╣╬╚╩╝═╪┌┬┐├┼┤└┴┘╭╮╰╯─┼╂".split("").fold(it) { acc, s -> | |
if (s.isEmpty()) acc else acc.replace(s[0], ' ') | |
} | |
} | |
.let { | |
"┃║".split("").fold(it) { acc, s -> | |
if (s.isEmpty()) acc else acc.replace(s[0], '│') | |
} | |
} | |
.replace(Regex("\n\\s*", RegexOption.MULTILINE), "\n") | |
.trim() | |
.split("\n") | |
.joinToString("\n") { row -> | |
if (row.matches(Regex(".*│\\s*$"))) | |
row.replace(Regex("^\\s*│"), "") | |
.replace(Regex("│\\s*$"), "") | |
else row | |
} | |
private val map: Map<List<List<String>>, String> = machineReadable | |
.substringAfter('\n') | |
.split('\n').map { row -> | |
row.substringBeforeLast('│').split('│').map { | |
it.split('|').map { it.trim() } | |
} to row.substringAfterLast('│').trim() | |
}.toMap() | |
val stripesNames: List<String> = machineReadable | |
.substringBefore('\n').split('│').map { it.trim() } | |
private val argsCount: Int = map.keys.first().size + 1 | |
inline fun <reified T> kotlinValueOf(): KFunction<*>? = | |
T::class.declaredFunctions.firstOrNull { | |
it.name == "valueOf" && | |
it.parameters.map { it.type } == listOf(String::class) | |
} | |
inline fun <reified T> javaValueOf(): Method? = | |
T::class.java.declaredMethods.firstOrNull { | |
it.name == "valueOf" && | |
it.parameters.map { it.type } == listOf(String::class.java) | |
} | |
inline fun <reified T> companionObjectOf(bestValueFor: String) = | |
T::class.companionObject!!.memberProperties.find { it.name == bestValueFor }!! | |
.call(T::class.companionObjectInstance) as T | |
fun bestValueFor(params: List<Any?>) = | |
map.entries.maxByOrNull { entry -> | |
params.mapIndexed { argNumber, param -> | |
val match = 10.toDouble().pow(argsCount - argNumber.toDouble()).toLong() | |
when { | |
param.toString() in entry.key[argNumber] -> match | |
entry.key[argNumber].all { it.startsWith("!") } && | |
param.toString() !in entry.key[argNumber].map { it.substring(1) } -> match | |
entry.key[argNumber].all { it.isEmpty() } -> 0 | |
else -> -1 | |
} | |
} | |
// uncomment the line below to debug (by looking at the highest value in the logs) | |
// .also { println(entry.toString() + " " + if(it.all{ it >= 0 }) it.sum() else -1) } | |
.takeIf { it.all { it >= 0 } }?.sum() ?: -1 | |
}!!.value | |
fun asList(params: Map<String, String>) = | |
params.entries.sortedBy { stripesNames.indexOf(it.key) }.map { it.value } | |
inline fun <reified T> `🔀`(vararg params: Any) = `🔀`<T>(params.toList()) | |
inline infix fun <reified T> `🔀`(params: Map<String, String>) = | |
`🔀`<T>(asList(params)) | |
inline infix fun <reified T> `🔀`(params: List<*>) = | |
bestValueFor(params).let { | |
it.takeIf { T::class == String::class } as T? | |
?: kotlinValueOf<T>()?.call(it) as T? | |
?: javaValueOf<T>()?.invoke(null, it) as T? | |
?: companionObjectOf(it) | |
} | |
} |
package org.toilelibre.libe | |
import org.junit.Test | |
import org.toilelibre.libe.RouterTest.Decision.* | |
import java.security.KeyFactory | |
import java.security.KeyStore | |
import kotlin.test.assertEquals | |
class RouterTest { | |
@Test | |
fun noTable() { | |
assertEquals("", Router("").`🔀`("")) | |
} | |
@Test | |
fun onlyHeader() { | |
val header = | |
"""day of the week | time of the day | message | |
────────────────┼─────────────────┼──────────────────── | |
""" | |
Router(header).`🔀`<String>() | |
} | |
@Test | |
fun withTexts() { | |
val messages = Router(""" | |
day of the week | time of the day | message | |
────────────────┼─────────────────┼──────────────────── | |
Monday │ morning │ Good luck | |
│ morning │ Good morning | |
│ afternoon │ Good afternoon | |
│ evening │ Good evening | |
│ night │ Good night | |
Friday │ evening │ Have a good weekend | |
Saturday │ night │ Have fun | |
""") | |
assertEquals("Good luck", | |
messages.`🔀`("Monday", "morning")) | |
assertEquals("Good morning", | |
messages.`🔀`("Wednesday", "morning")) | |
assertEquals("Good evening", | |
messages.`🔀`("Saturday", "evening")) | |
} | |
enum class Decision { | |
PASS, REJECT | |
} | |
@Test | |
fun withEnums() { | |
val exams = Router(""" | |
Maths | Physics | English | Sports | Decision | |
─────────┼─────────┼─────────┼────────┼───────── | |
│ │ │ │ $REJECT | |
$REJECT │ │ │ │ $REJECT | |
│ $REJECT │ $PASS │ $PASS │ $PASS | |
$PASS │ $PASS │ │ │ $PASS | |
""") | |
assertEquals(REJECT, | |
exams.`🔀`(REJECT, null, null, null)) | |
assertEquals(REJECT, | |
exams.`🔀`(REJECT, PASS, PASS, PASS)) | |
assertEquals(PASS, | |
exams.`🔀`(PASS, REJECT, PASS, PASS)) | |
assertEquals(REJECT, | |
exams.`🔀`(null, null, null, null)) | |
} | |
companion object { | |
val RSA = KeyFactory.getInstance("RSA")!! | |
val JKS = KeyStore.getInstance("JKS")!! | |
val PKCS12 = KeyStore.getInstance("PKCS12")!! | |
} | |
@Test | |
fun useCompanionObject() { | |
val router = Router(""" | |
arch │ hasJava │ has.NET │ choose | |
───────────┼─────────┼─────────┼───────── | |
│ │ │ RSA | |
│ yes │ │ JKS | |
win │ │ │ PKCS12 | |
win|linux │ │ yes │ PKCS12 | |
""") | |
assertEquals(RSA, | |
router.route<RouterTest, KeyFactory>("linux", "no", "no")) | |
assertEquals(PKCS12, | |
router.route<RouterTest, KeyStore>("linux", "no", "yes")) | |
assertEquals(PKCS12, | |
router.route<RouterTest, KeyStore>("win", "no", "yes")) | |
assertEquals(PKCS12, | |
router.route<RouterTest, KeyStore>("win", "yes", "no")) | |
assertEquals(JKS, | |
router.route<RouterTest, KeyStore>("linux", "yes", "no")) | |
} | |
@Test | |
fun returningBoolean() { | |
val router = Router( | |
""" | |
Left │ Right │ XOr | |
────────┼─────────┼────── | |
false │ false │ false | |
false │ true │ true | |
true │ false │ true | |
true │ true │ false | |
""" | |
) | |
val result: Boolean = router.`🔀`(false, true) | |
expectThat(result).isTrue() | |
} | |
@Test | |
fun returningNumber() { | |
val router = Router( | |
""" | |
Country │ Colors | |
──────────────┼───────── | |
France │ 3 | |
Russia │ 3 | |
Austria │ 2 | |
South Africa │ 6 | |
│ -1 | |
""" | |
) | |
val france: Int = router `🔀` listOf("France") | |
val ireland: Int = router `🔀` listOf("Ireland") | |
expectThat(france).isEqualTo(3) | |
expectThat(ireland).isEqualTo(-1) | |
} | |
@Test | |
fun worksInAnyStripesOrder() { | |
val router = Router( | |
""" | |
arg1 │ arg2 │ arg3 │ arg4 │ arg5 │ arg6 │ value | |
──────┼───────┼───────┼──────┼───────┼───────┼───────────────── | |
one │ two │ three │ four │ five │ six │ true | |
│ │ │ │ │ │ false | |
""" | |
) | |
val shouldBeTrue: Boolean = router `🔀` mapOf( | |
"arg3" to "three", | |
"arg5" to "five", | |
"arg2" to "two", | |
"arg4" to "four", | |
"arg1" to "one" | |
) | |
expectThat(shouldBeTrue).isTrue() | |
} | |
@Test | |
fun separatorsCanBeAdded() { | |
val router = Router( | |
""" | |
Country │ Continent | |
═══════════════╪════════════════ | |
UnitedStates │ NorthAmerica | |
Canada │ NorthAmerica | |
───────────────┼──────────────── | |
China │ Asia | |
Singapore │ Asia | |
India │ Asia | |
Laos │ Asia | |
───────────────┼──────────────── | |
Austria │ Europe | |
France │ Europe | |
═══════════════╪════════════════ | |
""" | |
) | |
expectThat(router.`🔀`<String>("UnitedStates")) | |
.isEqualTo("NorthAmerica") | |
expectThat(router.`🔀`<String>("Canada")) | |
.isEqualTo("NorthAmerica") | |
expectThat(router.`🔀`<String>("China")) | |
.isEqualTo("Asia") | |
expectThat(router.`🔀`<String>("Singapore")) | |
.isEqualTo("Asia") | |
expectThat(router.`🔀`<String>("India")) | |
.isEqualTo("Asia") | |
expectThat(router.`🔀`<String>("Laos")) | |
.isEqualTo("Asia") | |
expectThat(router.`🔀`<String>("Austria")) | |
.isEqualTo("Europe") | |
expectThat(router.`🔀`<String>("France")) | |
.isEqualTo("Europe") | |
} | |
@Test | |
fun tableWithBorders() { | |
val router = Router( | |
""" | |
╭──────────┬────────────────╮ | |
│ Number │ Pluralization │ | |
├──────────┼────────────────┤ | |
│ 0 │ None │ | |
│ 1 │ One │ | |
│ 2 │ A couple │ | |
│ 3 │ Three │ | |
│ 4|5|6 │ A few │ | |
│ │ Many │ | |
╰──────────┴────────────────╯ | |
""" | |
) | |
expectThat( | |
router.`🔀`<String>(0) | |
).isEqualTo("None") | |
expectThat( | |
router.`🔀`<String>(3) | |
).isEqualTo("Three") | |
expectThat( | |
router.`🔀`<String>(5) | |
).isEqualTo("A few") | |
expectThat( | |
router.`🔀`<String>(8) | |
).isEqualTo("Many") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment