Petro Semeniuk
Petro Semeniuk

Reputation: 7038

How to get map of field names to field types of case class with shapeless

I browsed pretty much all tutorials and have "The Type Astronauts" on my desk but I still can't figure out how to write snippet which can extract map of names to types using shapeless from case class. To be precise I'm after following interface:

case class Sample(id: String, date: LocalDate)

def sampleExpectedMetadata = expectedMetadata[Sample] // = ("id" -> "String", "date" -> "LocalDate")

def expectedMetadata[T]: Map[String, String] = ???

Help from anyone who can assist with writing correct implementation would be greatly appreciated.

Upvotes: 1

Views: 902

Answers (2)

Alec
Alec

Reputation: 32309

Did you have something like this in mind?

import shapeless._
import shapeless.record._
import shapeless.labelled._

object expectedMetadata {
  def apply[T](implicit g: GetFieldTypes[T]): Map[String, String] = g.getFieldTypes

  sealed trait GetFieldTypes[T] {
    def getFieldTypes: Map[String,String]
  }

  implicit val hnil = new GetFieldTypes[HNil] {
    def getFieldTypes = Map.empty
  }

  implicit def hcons[K <: Symbol, V, T <: HList](implicit
    wit: Witness.Aux[K],
    typ: Typeable[V],
    rest: GetFieldTypes[T]
  ) = new GetFieldTypes[FieldType[K, V] :: T] {
    def getFieldTypes = rest.getFieldTypes + (wit.value.name -> typ.describe)
  }

  implicit def caseClass[T, G](implicit
    lg: LabelledGeneric.Aux[T, G],
    rest: GetFieldTypes[G]
  ) = new GetFieldTypes[T] {
    def getFieldTypes = rest.getFieldTypes
  }
}

Sample use:

scala> case class Sample(id: String, date: LocalDate)
scala> expectedMetadata[Sample]
res1: Map[String,String] = Map(date -> LocalDate, id -> String)

Upvotes: 3

Daniel Hinojosa
Daniel Hinojosa

Reputation: 992

I could be wrong that Shapeless would have nothing to do with this sort of introspection, using the answer from Get field names list from case class and refining it, your answer would be:

import scala.reflect.runtime.universe._

def expectedMetadata[T](implicit t:TypeTag[T]):Map[String, String] = 
   t.tpe.members
    .collect { case m:MethodSymbol if m.isCaseAccessor => m }
    .map(x => x.name.toString -> x.returnType.toString)
    .toMap

Where, expectedMetadata[Sample] will return Map[String,String] = Map(date -> java.time.LocalDate, id -> String)

Upvotes: 1

Related Questions