Skip to content

Instantly share code, notes, and snippets.

@afiore
Last active August 22, 2018 19:21
Show Gist options
  • Save afiore/5e14ef88d7277c8f0a000e35d62051fd to your computer and use it in GitHub Desktop.
Save afiore/5e14ef88d7277c8f0a000e35d62051fd to your computer and use it in GitHub Desktop.
Filtering non-optional fields using shapeless
import shapeless._
import shapeless.labelled._
trait Required[A] {
def isRequired: Boolean
}
object Required {
implicit def optIsRequired[A]: Required[Option[A]] = new Required[Option[A]] {
override def isRequired = false
}
implicit def otherRequired[A]: Required[A] = new Required[A] {
override def isRequired = true
}
def isRequired[A: Required] = implicitly[Required[A]].isRequired
trait Fields[A] {
def get: List[String]
}
object Fields {
def instance[A](fs: List[String]): Fields[A] = new Fields[A] {
override def get: List[String] = fs
}
implicit val hnilFields: Fields[HNil] = instance(Nil)
implicit def hlistFields[K <: Symbol, H, T <: HList](
implicit
witness: Witness.Aux[K],
req: Lazy[Required[H]],
tFields: Fields[T])
: Fields[FieldType[K, H] :: T] = instance {
if (req.value.isRequired) {
witness.value.name :: tFields.get
} else tFields.get
}
implicit def genericFields[A, R](
implicit
gen: LabelledGeneric.Aux[A, R],
rfs: Fields[R])
: Fields[A] = instance(rfs.get)
def apply[L](implicit ev: Fields[L]): List[String] = ev.get
}
}
type Person =
FieldType[Witness.`'name`.T, String] ::
FieldType[Witness.`'age`.T, Option[Int]] :: HNil
case class Employee(number: Int, lineManages: Option[List[Employee]])
assert(Required.Fields[Person] == List("name"))
assert(Required.Fields[Employee] == List("number"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment