Created
January 30, 2020 14:07
-
-
Save rolandjohann/038e349516514adf105c551c7667544e to your computer and use it in GitHub Desktop.
shapeless doobie helper to generate fragments :: WIP
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
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