Last active
December 10, 2020 22:13
-
-
Save ChristopherDavenport/02217ed65898175577ae3feed19ee86a to your computer and use it in GitHub Desktop.
Generic Show Derivation of Case Classes using Scala 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.chrisdavenport.shapelessless | |
import cats._ | |
import cats.syntax.all._ | |
import cats.effect._ | |
import scala.deriving._ | |
import scala.compiletime._ | |
import org.tpolecat.typename._ | |
// Primarily from learning from | |
// https://github.com/scodec/scodec/blob/5e46cc8d85f8ad99bf65983f86f24076568a492c/shared/src/main/scala/scodec/Codec.scala#L633-L634 | |
object Main extends IOApp: | |
case class Foo(i: Int, b: String) | |
import io.chrisdavenport.shapelessless.CaseClassFun.{_, given} | |
def run(args: List[String]): IO[ExitCode] = { | |
val foo = Foo(1, "bar") | |
val x = summon[Show[Foo]] | |
// IO(println("foo")) | |
IO(println(show"$foo")) // Foo(i=1,b=bar) | |
.as(ExitCode.Success) | |
} | |
object CaseClassFun: | |
private inline def encodeElems[A <: Tuple, L <: Tuple](a: Any, i: Int): String = | |
inline erasedValue[A] match { | |
case _: (hd *: EmptyTuple) => | |
inline erasedValue[L] match { | |
case _ :(hdLabel *: tlLabels) => | |
val hdLabelValue = constValue[hdLabel].asInstanceOf[String] | |
val show = summonInline[Show[hd]] | |
hdLabelValue ++ "=" ++ | |
show.show(a.asInstanceOf[Product].productElement(i).asInstanceOf[hd]) | |
case EmptyTuple => sys.error("not possible - label for product not available") | |
} | |
case _: (hd *: tl) => | |
inline erasedValue[L] match { | |
case _ :(hdLabel *: tlLabels) => | |
val hdLabelValue = constValue[hdLabel].asInstanceOf[String] | |
val show = summonInline[Show[hd]] | |
hdLabelValue ++ "=" ++ | |
show.show(a.asInstanceOf[Product].productElement(i).asInstanceOf[hd]) ++ "," ++ | |
encodeElems[tl, tlLabels](a, i + 1) | |
case EmptyTuple => sys.error("not possible - label for product not available") | |
} | |
case EmptyTuple => "" | |
} | |
inline given [P <: Product]( | |
using | |
t: TypeName[P], | |
m: Mirror.ProductOf[P], | |
) as Show[P] = | |
{(p: P) => | |
simpleTypeName(t.value) ++ "(" ++ | |
encodeElems[m.MirroredElemTypes, m.MirroredElemLabels](p, 0) | |
++ ")" | |
} | |
// scala.collection.immutable.List[scala.Option[scala.Int]] => List | |
// io.chrisdavenport.shapelessless.Main.Foo => Foo | |
def simpleTypeName(string: String): String = { | |
val stop = string.indexOf("[") | |
val topClass = | |
if (stop >= 0) string.substring(0, stop) | |
else string | |
val indexOfPeriodInTop = topClass.lastIndexOf(".") | |
val simpleTopType = if (indexOfPeriodInTop >= 0) topClass.substring(indexOfPeriodInTop + 1, topClass.length) | |
else topClass | |
simpleTopType | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment