package com.twitter.finagle.postgres.generic import scala.annotation.implicitNotFound import scala.collection.GenTraversable import com.twitter.finagle.postgres.Param import com.twitter.finagle.postgres.values.ValueEncoder import shapeless.ops.hlist.{Length, LiftAll, Mapper, ToList, ToTraversable, Tupler, Unifier, Zip} import shapeless.ops.nat.ToInt import shapeless.ops.record.Keys import shapeless.{Generic, HList, HNil, LUBConstraint, LabelledGeneric, Nat, Poly1} /** * Typeclass allowing conversion of a type T into a sequence of postgres parameters * Used for quoting lists, tuples, etc in a query (i.e. "WHERE foo IN ${("A", "B")}) */ @implicitNotFound( """Could not represent the given value(s) of type ${T} as query parameters. The value must either be a scalar with a ValueEncoder instance, a Seq whose type parameter has a ValueEncoder instance, or a homogeneous tuple whose type has a ValueEncoder instance.""" ) trait QueryParams[T] { def apply(t: T): Seq[Param[_]] def placeholders(t: T, start: Int): Seq[String] } object QueryParams extends QueryParams0 { implicit def seq[F[A] <: Seq[A], T](implicit encoder: ValueEncoder[T]): QueryParams[F[T]] = new QueryParams[F[T]] { @inline final def apply(ts: F[T]): Seq[Param[_]] = ts.map(t => Param(t)) @inline final def placeholders(ts: F[T], start: Int) = (start until (start + ts.length)).map(i => s"$$$i") } } trait QueryParams0 extends QueryParams1 { self: QueryParams.type => implicit def tuple[A <: Product, L <: HList, P <: HList, N <: Nat](implicit gen: Generic.Aux[A, L], length: Length.Aux[L, N], tupler: Tupler.Aux[L, A], toInt: ToInt[N], mapper: Mapper.Aux[toParam.type, L, P], toTraversable: ToTraversable.Aux[P, Seq, Param[_]] ): QueryParams[A] = new QueryParams[A] { @inline final def apply(a: A): Seq[Param[_]] = toTraversable(mapper(gen.to(a))) @inline final def placeholders(a: A, start: Int) = (start until (start + toInt())).map(i => s"$$$i") } } trait QueryParams1 { self: QueryParams.type => implicit def single[T](implicit encoder: ValueEncoder[T]): QueryParams[T] = new QueryParams[T] { def apply(t: T): Seq[Param[T]] = Seq(Param(t)) def placeholders(t: T, start: Int) = Seq(s"$$$start") } }