Skip to content

Instantly share code, notes, and snippets.

@ahoy-jon
Last active February 29, 2020 16:42
Show Gist options
  • Save ahoy-jon/9b55ede1fb838844319140321d285705 to your computer and use it in GitHub Desktop.
Save ahoy-jon/9b55ede1fb838844319140321d285705 to your computer and use it in GitHub Desktop.
sealed trait Path
case class Field(name: String, child: Option[Path]) extends Path {
override def toString: String = child match {
case None => name
case Some(path) => s"$name.$path"
}
}
/**
* Path for option : Person(name:String, **age:Option[Int]**)
*
* @param child
*/
case class OptPath(child: Path) extends Path {
override def toString: String = s"?.$child"
}
case class SeqPath(child: Path) extends Path {
override def toString: String = s"[].$child"
}
trait Destruct[X] {
def fields: Seq[Path]
}
object Destruct {
type Typeclass[X] = Destruct[X]
def apply[T: Typeclass]: Typeclass[T] = implicitly
private def instance[X](fields: Seq[Path]): Typeclass[X] = {
val _f = fields
new Typeclass[X] {
override def fields: Seq[Path] = _f
}
}
implicit def optDestruct[T: Destruct]: Destruct[Option[T]] = instance(Destruct[T].fields.map(OptPath))
implicit def seqDestruct[T: Destruct]: Destruct[Seq[T]] = instance(Destruct[T].fields.map(SeqPath))
def plop[T]: Typeclass[T] = instance(Nil)
implicit val intDestruct: Typeclass[Int] = plop
implicit val stringDestruct: Typeclass[String] = plop
implicit val booleanDestruct: Typeclass[Boolean] = plop
// ...
/** 🎶 Les magnolias sont toujours là 🎶 **/
import magnolia._
import scala.language.experimental.macros
def combine[A](ctx: CaseClass[Typeclass, A]): Destruct[A] = instance(
ctx.parameters.flatMap(param => {
param.typeclass.fields match {
case fields if fields.isEmpty => Seq(Field(param.label, None))
case fields => fields.map(f => Field(param.label, Some(f)))
}
})
)
implicit def gen[A]: Destruct[A] = macro Magnolia.gen[A]
implicit class DestructOps[CC <: Product](cc: CC)(implicit XXX: Destruct[CC]) {
def fields: Seq[Path] = XXX.fields
}
}
object Prg {
def main(args: Array[String]): Unit = {
import Destruct._
case class Foo(i: Int, b: Boolean)
//via ops
println(Foo(1, true).fields)
/*
ArrayBuffer(i, b)
*/
case class Address(city: String, country: String)
case class Person(name: String, address: Option[Address])
println(Destruct[Person].fields)
/*
ArrayBuffer(name, address.?.city, address.?.country)
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment