Skip to content

Instantly share code, notes, and snippets.

@rolandjohann
Created January 30, 2020 14:07
Show Gist options
  • Save rolandjohann/038e349516514adf105c551c7667544e to your computer and use it in GitHub Desktop.
Save rolandjohann/038e349516514adf105c551c7667544e to your computer and use it in GitHub Desktop.
shapeless doobie helper to generate fragments :: WIP
package dao.queries
import doobie.util.fragment.Fragment
import doobie.implicits._
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Keys
trait FragmentEncoder[T] {
def encode(value: T): List[Fragment]
}
object FragmentEncoder {
def apply[T](implicit encoder: FragmentEncoder[T]): FragmentEncoder[T] = encoder
}
object FragmentEncoders {
def createEncoder[T](fn: T => List[Fragment]): FragmentEncoder[T] = {
new FragmentEncoder[T] {
override def encode(value: T): List[Fragment] = fn(value)
}
}
implicit val stringFragmentEncoder = createEncoder[String](value => List(value.fr))
implicit val intFragmentEncoder = createEncoder[Int](value => List(value.fr))
implicit val longFragmentEncoder = createEncoder[Long](value => List(value.fr))
implicit val bigDecimalFragmentEncoder = createEncoder[BigDecimal](value => List(value.fr))
implicit val booleanFragmentEncoder = createEncoder[Boolean](value => List(value.fr))
implicit def optionFragmentEncoder[T](implicit enc: FragmentEncoder[T]) = {
createEncoder[Option[T]](option => List(fr"$option"))
}
implicit val hnilFragmentEncoder = createEncoder[HNil](_ => Nil)
implicit def hlistEncoder[Key <: Symbol, Head, Tail <: HList](
implicit
witness: Witness.Aux[Key],
hEncoder: Lazy[FragmentEncoder[Head]],
tEncoder: FragmentEncoder[Tail]
): FragmentEncoder[FieldType[Key, Head] :: Tail] = {
val fieldName = witness.value.name
// todo: fieldName currently is not in use. we need to implement different encoders for this
createEncoder { hlist =>
val head = hEncoder.value.encode(hlist.head)
val tail = tEncoder.encode(hlist.tail)
head ++ tail
}
}
implicit def genericObjectFragmentEncoder[A, Head](
implicit
generic: LabelledGeneric.Aux[A, Head],
hEncoder: Lazy[FragmentEncoder[Head]]
): FragmentEncoder[A] = {
createEncoder { value =>
hEncoder.value.encode(generic.to(value))
}
}
}
trait FieldNameReader[T] {
def read: List[String]
override def toString: String = read.mkString(", ")
}
object FieldNameReader extends LabelledProductTypeClassCompanion[FieldNameReader] {
def apply[T](fs: List[String]): FieldNameReader[T] = new FieldNameReader[T] {
override def read: List[String] = fs
}
implicit val string: FieldNameReader[String] = apply[String](Nil)
implicit def anyVal[T <: AnyVal]: FieldNameReader[T] = apply[T](Nil)
object typeClass extends LabelledProductTypeClass[FieldNameReader] {
override def product[H, T <: HList](name: String, ch: FieldNameReader[H], ct: FieldNameReader[T]) : FieldNameReader[H :: T] =
FieldNameReader(name :: ct.read)
override def emptyProduct : FieldNameReader[HNil] = FieldNameReader(Nil)
override def project[F, G](instance: => FieldNameReader[G], to: F => G, from: G => F): FieldNameReader[F] = FieldNameReader(instance.read)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment