Skip to content

Instantly share code, notes, and snippets.

@hohonuuli
Last active November 2, 2021 20:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hohonuuli/cf5693684e949925fa15b4916a9bb1d3 to your computer and use it in GitHub Desktop.
Save hohonuuli/cf5693684e949925fa15b4916a9bb1d3 to your computer and use it in GitHub Desktop.
Method to convert case classes to CSV
import java.net.URL
import scala.deriving.*
import scala.compiletime.{summonAll}
import java.nio.file.Path
/**
* Base function we use to convert case classes to CSV
* @param a The object to convert
*/
def transform[A : Transformer](a: A) = summon[Transformer[A]].f(a)
// Base trait
trait Transformer[T]:
def f(t: T): String
// Create a type class of T => String for every type in your case class
given Transformer[String] with
def f(x: String) = x
given Transformer[Int] with
def f(x: Int) = x.toString
given Transformer[Double] with
def f(x: Double) = f"$x%.2f"
given Transformer[URL] with
def f(x: URL) = x.toExternalForm
given Transformer[Path] with
def f(x: Path) = x.getFileName.toString
given [T] (using t: Transformer[T]): Transformer[Option[T]] =
new Transformer[Option[T]]:
def f(x: Option[T]) = x match
case None => ""
case Some(x) => t.f(x)
/**
* Transforms a list of case classes into CSV data, including header row
*/
given [A <: Product] (using t: Transformer[A]): Transformer[List[A]] =
new Transformer[List[A]]:
def f(x: List[A]) =
val rows = asHeader(x.head) :: x.map(transform)
rows.mkString("\n")
/**
* Turns a case class into a CSV row string
*
* Fucking voodoo from https://kavedaa.github.io/auto-ui-generation/auto-ui-generation.html
*/
inline given [A <: Product] (using m: Mirror.ProductOf[A]): Transformer[A] =
new Transformer[A]:
type ElemTransformers = Tuple.Map[m.MirroredElemTypes, Transformer]
val elemTransformers = summonAll[ElemTransformers].toList.asInstanceOf[List[Transformer[Any]]]
def f(a: A): String =
val elems = a.productIterator.toList
val transformed = elems.zip(elemTransformers) map { (elem, transformer) => transformer.f(elem) }
transformed.mkString(",")
/**
* Turns a case class into a CSV header row string
*/
def asHeader[A <: Product](a: A): String = a.productElementNames.toList.mkString(",")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment