Last active
May 25, 2017 07:25
-
-
Save mikla/98f40fd916cec5fea762114b63123aa0 to your computer and use it in GitHub Desktop.
Type class that extracts information about all fields into Map[String, String].
This file contains hidden or 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
| /** | |
| * Type class that extracts information about all fields into Map[String, String]. | |
| * It's uses `PlainStringRecordValue` instances for particular type of the field to obtain string representation. | |
| * @tparam T | |
| */ | |
| @typeclass trait PlainStringRecord[T] { | |
| def plainStringRecord(t: T): Map[String, String] | |
| } | |
| object PlainStringRecord extends EventInfoLowerPriorityImplicits { | |
| implicit def infoHNil: PlainStringRecord[HNil] = new PlainStringRecord[HNil] { | |
| override def plainStringRecord(t: HNil): Map[String, String] = Map.empty | |
| } | |
| implicit def infoHCons[K <: Symbol, H, T <: HList]( | |
| implicit key: Witness.Aux[K], | |
| sh: Lazy[PlainStringRecordValue[H]], | |
| st: Lazy[PlainStringRecord[T]] | |
| ): PlainStringRecord[FieldType[K, H] :: T] = new PlainStringRecord[FieldType[K, H] :: T] { | |
| override def plainStringRecord(t: FieldType[K, H] :: T): Map[String, String] = { | |
| val head = Map(key.value.name -> sh.value.plainString(t.head)) | |
| val tail = st.value.plainStringRecord(t.tail) | |
| head ++ tail | |
| } | |
| } | |
| implicit def infoCNil: PlainStringRecord[CNil] = new PlainStringRecord[CNil] { | |
| override def plainStringRecord(t: CNil): Map[String, String] = t.impossible | |
| } | |
| implicit def infoCCons[K <: Symbol, H, T <: Coproduct]( | |
| implicit key: Witness.Aux[K], | |
| sh: PlainStringRecord[H], | |
| st: PlainStringRecord[T] | |
| ): PlainStringRecord[FieldType[K, H] :+: T] = new PlainStringRecord[FieldType[K, H] :+: T] { | |
| override def plainStringRecord(t: FieldType[K, H] :+: T): Map[String, String] = t match { | |
| case Inl(l) => sh.plainStringRecord(l) | |
| case Inr(r) => st.plainStringRecord(r) | |
| } | |
| } | |
| } | |
| trait EventInfoLowerPriorityImplicits { | |
| implicit def infoGeneric[T, Repr]( | |
| implicit gen: LabelledGeneric.Aux[T, Repr], | |
| sg: Lazy[PlainStringRecord[Repr]]): PlainStringRecord[T] = new PlainStringRecord[T] { | |
| override def plainStringRecord(t: T): Map[String, String] = sg.value.plainStringRecord(gen.to(t)) | |
| } | |
| } | |
| @typeclass trait PlainStringRecordValue[T] { | |
| def plainString(t: T): String | |
| } | |
| object PlainStringRecordValue extends PlainStringRecordValueLowerPriorityImplicits1 { | |
| // put implicits here | |
| } | |
| trait PlainStringRecordValueLowerPriorityImplicits1 extends PlainStringRecordValueLowerPriorityImplicits2 { | |
| implicit def optionPlainValue[T]( | |
| implicit V: PlainStringRecordValue[T]) = new PlainStringRecordValue[Option[T]] { | |
| override def plainString(t: Option[T]): String = t.map(e => V.plainString(e)).getOrElse("-") | |
| } | |
| } | |
| trait PlainStringRecordValueLowerPriorityImplicits2 { | |
| implicit def plainStringValueAny[T]: PlainStringRecordValue[T] = new PlainStringRecordValue[T] { | |
| override def plainString(t: T): String = t.toString | |
| } | |
| } | |
| // test | |
| import org.scalatest.{FunSuite, Matchers} | |
| class PlainStringRecordSpec extends FunSuite with Matchers { | |
| case class Value(v: String) | |
| case class Event(name: String, value: Value) | |
| test("PlainStringRecord should map representation of case class parameters and uses " + | |
| "PlainStringRecordValue type class instance to retrieve field info") { | |
| implicit val eventPlainStringValue: PlainStringRecordValue[Value] = new PlainStringRecordValue[Value] { | |
| override def plainString(t: Value): String = t.v | |
| } | |
| val event = Event("name", Value("value")) | |
| implicitly[PlainStringRecord[Event]].plainStringRecord(event) should be (Map("name" -> "name", "value" -> "value")) | |
| } | |
| test("PlainStringRecord should map representation of case class parameters and fallback to default .toString " + | |
| "if no implicit PlainStringRecordValue found for particular type") { | |
| val event = Event("name", Value("value")) | |
| implicitly[PlainStringRecord[Event]].plainStringRecord(event) should be { | |
| Map("name" -> "name", "value" -> "Value(value)") | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment