diff --git a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/show/Show.kt b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/show/Show.kt index 5c7c9387654..b044eabd46a 100644 --- a/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/show/Show.kt +++ b/kotest-assertions/kotest-assertions-shared/src/commonMain/kotlin/io/kotest/assertions/show/Show.kt @@ -17,6 +17,46 @@ interface Show { fun show(a: A): Printed } +/** + * Global object that allows for registration of custom [Show] typeclasses. + */ +object Shows { + + private val shows = mutableMapOf, Show<*>>().apply { + put(String::class, StringShow) + put(Map::class, MapShow) + put(BooleanArray::class, ArrayShow) + put(IntArray::class, ArrayShow) + put(ShortArray::class, ArrayShow) + put(FloatArray::class, ArrayShow) + put(DoubleArray::class, ArrayShow) + put(LongArray::class, ArrayShow) + put(ByteArray::class, ArrayShow) + put(CharArray::class, ArrayShow) + put(Array::class, ArrayShow) + put(List::class, ListShow()) + put(Iterable::class, IterableShow()) + put(Long::class, DefaultShow) + put(Int::class, DefaultShow) + put(Short::class, DefaultShow) + put(Byte::class, DefaultShow) + put(Double::class, DefaultShow) + put(Float::class, DefaultShow) + put(Boolean::class, DefaultShow) + put(KClass::class, KClassShow) + } + + fun add(kclass: KClass, show: Show) { + shows[kclass] = show + } + + fun remove(kclass: KClass<*>) { + shows.remove(kclass) + } + + fun all() = shows.toMap() +} + /** * Represents a value that has been appropriately formatted for display in output logs or error messages. * For example, a null might be formatted as . @@ -32,27 +72,16 @@ fun showFor(t: T): Show = platformShow(t) ?: commonShowFor(t) expect fun platformShow(a: A): Show? @Suppress("UNCHECKED_CAST") -fun commonShowFor(t: T): Show = when (t) { - is String -> StringShow as Show - is Map<*, *> -> MapShow as Show - is BooleanArray -> ArrayShow - is IntArray -> ArrayShow - is ShortArray -> ArrayShow - is FloatArray -> ArrayShow - is DoubleArray -> ArrayShow - is LongArray -> ArrayShow - is ByteArray -> ArrayShow - is CharArray -> ArrayShow - is Array<*> -> ArrayShow - is List<*> -> ListShow() as Show - is Iterable<*> -> IterableShow() as Show - is Long, t is Boolean, t is Int, t is Double, t is Float, t is Short, t is Byte -> DefaultShow - is KClass<*> -> KClassShow as Show - else -> when { - // this won't work in JS or native, so they'll get the boring old toString version - io.kotest.mpp.reflection.isDataClass(t::class) -> dataClassShow() - else -> DefaultShow +fun commonShowFor(t: T): Show { + // lookup a show from the registered typeclasses + val kclass: KClass<*>? = Shows.all().keys.firstOrNull { it.isInstance(t) } + if (kclass != null) { + val show: Show<*>? = Shows.all()[kclass] + return show as Show } + // this won't work in JS or native, so they'll get the boring old toString version + if (io.kotest.mpp.reflection.isDataClass(t::class)) return dataClassShow() + return DefaultShow } internal fun recursiveRepr(root: Any, node: Any?): Printed {