Skip to content

Commit

Permalink
Allow registration of custom Show typeclasses #2021
Browse files Browse the repository at this point in the history
  • Loading branch information
sksamuel committed Jan 29, 2021
1 parent 2fb3188 commit 445c554
Showing 1 changed file with 47 additions and 21 deletions.
Expand Up @@ -17,6 +17,46 @@ interface Show<in A> {
fun show(a: A): Printed
}

/**
* Global object that allows for registration of custom [Show] typeclasses.
*/
object Shows {

private val shows = mutableMapOf<KClass<*>, 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<Any>())
put(Iterable::class, IterableShow<Any>())
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 <T : Any> add(kclass: KClass<out T>, show: Show<T>) {
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 <null>.
Expand All @@ -32,27 +72,13 @@ fun <T : Any> showFor(t: T): Show<T> = platformShow(t) ?: commonShowFor(t)
expect fun <A : Any> platformShow(a: A): Show<A>?

@Suppress("UNCHECKED_CAST")
fun <T : Any> commonShowFor(t: T): Show<T> = when (t) {
is String -> StringShow as Show<T>
is Map<*, *> -> MapShow as Show<T>
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<T>() as Show<T>
is Iterable<*> -> IterableShow<T>() as Show<T>
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<T>
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<T>()
else -> DefaultShow
}
fun <T : Any> commonShowFor(t: T): Show<T> {
// lookup a show from the registered typeclasses
val show = Shows.all().keys.firstOrNull { it.isInstance(t) }
if (show != null) return show as Show<T>
// 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<T>()
return DefaultShow
}

internal fun recursiveRepr(root: Any, node: Any?): Printed {
Expand Down

0 comments on commit 445c554

Please sign in to comment.