I am attempting to derive a type class for serializing a case class to a query string. There is a twist though - lists are not encoded the normal way (as far as I can tell what the "normal" way is) but like below, with the field name of the list incorporated.
case class Example(attributes: List[String])
val example = Example(List("foo", "bar"))
encode(example) // attributes.1=foo&attributes.2=bar
I have something very basic which works for primitives, now I need some ideas of a way to get lists working as expected though.
trait Encoder[T] {
def encode(value: T): String
}
object Encoder {
def apply[T](implicit encoder: Encoder[T]): Encoder[T] = encoder
}
def createEncoder[A](fn: A => String): Encoder[A] =
(value: A) => fn(value)
implicit def hlistEncoder[K <: Symbol, H, T <: HList](
implicit
witness: Witness.Aux[K],
hEncoder: Lazy[Encoder[H]],
tEncoder: Encoder[T]
): Encoder[FieldType[K, H] :: T] = {
val fieldName: String = witness.value.name
createEncoder { hlist =>
val head = hEncoder.value.encode(hlist.head)
hlist.tail match {
case HNil => s"$fieldName=$head"
case _ =>
val tail = tEncoder.encode(hlist.tail)
s"$fieldName=$head&$tail"
}
}
}
implicit def genericEncoder[A, H](
implicit
generic: LabelledGeneric.Aux[A, H],
hEncoder: Lazy[Encoder[H]]
): Encoder[A] =
createEncoder { value =>
hEncoder.value.encode(generic.to(value))
}
implicit val intEncoder: Encoder[Int] = createEncoder(_.toString)
implicit val strEncoder: Encoder[String] = createEncoder(identity)
implicit val boolEncoder: Encoder[Boolean] = createEncoder(_.toString)
implicit val hnilEncoder: Encoder[HNil] = createEncoder(_ => "")
Thanks!