All Downloads are FREE. Search and download functionalities are using the official Maven repository.

doobieroll.syntax.ToAssemblerSyntax.scala Maven / Gradle / Ivy

There is a newer version: 0.3.2
Show newest version
package doobieroll.syntax

import cats.Monad
import cats.implicits._
import doobieroll.impl._
import doobieroll.{Assembler, LeafDef, ParentAssembler, ParentDef}
import shapeless._

import scala.annotation.tailrec

trait ToAssemblerSyntax {

  implicit class LeafDefExtension[F[_], A, ADb](leafDef: LeafDef[F, A, ADb]) {
    def toAssembler: Assembler[F, A, ADb :: HNil] = {
      new Assembler[F, A, ADb :: HNil] {
        private[doobieroll] override def makeVisitor(
          accum: Accum,
          idx: Int,
        ): Visitor[F, A, ADb :: HNil] =
          new LeafVisitorImpl[F, A, ADb](
            leafDef = leafDef,
            accum = accum,
            startIdx = idx,
          )
      }

    }
  }

  import shapeless._
  import shapeless.ops.hlist._

  final type Flattener[HL <: HList, Out <: HList] = FlatMapper.Aux[HListIdentity.type, HL, Out]

  object HListIdentity extends Poly1 {
    implicit def caseHList[HL <: HList] = at[HL](identity)
  }

  private def mkParent[F[_], A, ADb, CDbs <: HList, CDbsFlattened <: HList](
    par: ParentDef[F, A, ADb],
    assemblers: Vector[Assembler[F, Any, HList]],
    flattener: Flattener[CDbs, CDbsFlattened],
    FMonad: Monad[F],
  ): ParentAssembler[F, A, ADb :: CDbsFlattened] = {
    val _ = flattener // unused. For type inference only

    new ParentAssembler[F, A, ADb :: CDbsFlattened] {

      private[doobieroll] override def makeVisitor(
        accum: Accum,
        idx: Int,
      ): ParentVisitor[F, A, ADb :: CDbsFlattened] =
        new ParentVisitorImpl[F, A, ADb, CDbsFlattened](
          par,
          accum,
          idx,
          assemblers,
          FMonad,
        )
    }
  }

  @inline
  private def eraseAssemblerType[F[_], A, HL <: HList](
    assembler: Assembler[F, A, HL],
  ): Assembler[F, Any, HList] =
    assembler.asInstanceOf[Assembler[F, Any, HList]]

  implicit class ParentExtension[F[_], A, ADb, Cs <: HList](par: ParentDef.Aux[F, A, ADb, Cs]) {

    def toAssembler[C0, C0Dbs <: HList](
      c0Assembler: Assembler[F, C0, C0Dbs],
    )(implicit monadF: Monad[F]): ParentAssembler[F, A, ADb :: C0Dbs] =
      mkParent(
        par,
        Vector(eraseAssemblerType(c0Assembler)),
        implicitly[Flattener[C0Dbs :: HNil, C0Dbs]],
        monadF,
      )

    def toAssembler[C0, C1, C0Dbs <: HList, C1Dbs <: HList, CDbs <: HList](
      c0Assembler: Assembler[F, C0, C0Dbs],
      c1Assembler: Assembler[F, C1, C1Dbs],
    )(
      implicit monadF: Monad[F],
      flattener: Flattener[C0Dbs :: C1Dbs :: HNil, CDbs],
    ): ParentAssembler[F, A, ADb :: CDbs] =
      mkParent(
        par,
        Vector(eraseAssemblerType(c0Assembler), eraseAssemblerType(c1Assembler)),
        flattener,
        monadF,
      )

    def toAssembler[C0, C1, C2, C0Dbs <: HList, C1Dbs <: HList, C2Dbs <: HList, CDbs <: HList](
      c0Assembler: Assembler[F, C0, C0Dbs],
      c1Assembler: Assembler[F, C1, C1Dbs],
      c2Assembler: Assembler[F, C2, C2Dbs],
    )(
      implicit monadF: Monad[F],
      flattener: Flattener[C0Dbs :: C1Dbs :: C2Dbs :: HNil, CDbs],
    ): ParentAssembler[F, A, ADb :: CDbs] =
      mkParent(
        par,
        Vector(
          eraseAssemblerType(c0Assembler),
          eraseAssemblerType(c1Assembler),
          eraseAssemblerType(c2Assembler),
        ),
        flattener,
        monadF,
      )

    def toAssembler[
      C0,
      C1,
      C2,
      C3,
      C0Dbs <: HList,
      C1Dbs <: HList,
      C2Dbs <: HList,
      C3Dbs <: HList,
      CDbs <: HList,
    ](
      c0Assembler: Assembler[F, C0, C0Dbs],
      c1Assembler: Assembler[F, C1, C1Dbs],
      c2Assembler: Assembler[F, C2, C2Dbs],
      c3Assembler: Assembler[F, C3, C3Dbs],
    )(
      implicit monadF: Monad[F],
      flattener: Flattener[C0Dbs :: C1Dbs :: C2Dbs :: C3Dbs :: HNil, CDbs],
    ): ParentAssembler[F, A, ADb :: CDbs] =
      mkParent(
        par,
        Vector(
          eraseAssemblerType(c0Assembler),
          eraseAssemblerType(c1Assembler),
          eraseAssemblerType(c2Assembler),
          eraseAssemblerType(c3Assembler),
        ),
        flattener,
        monadF,
      )

    def toAssembler[
      C0,
      C1,
      C2,
      C3,
      C4,
      C0Dbs <: HList,
      C1Dbs <: HList,
      C2Dbs <: HList,
      C3Dbs <: HList,
      C4Dbs <: HList,
      CDbs <: HList,
    ](
      c0Assembler: Assembler[F, C0, C0Dbs],
      c1Assembler: Assembler[F, C1, C1Dbs],
      c2Assembler: Assembler[F, C2, C2Dbs],
      c3Assembler: Assembler[F, C3, C3Dbs],
      c4Assembler: Assembler[F, C4, C4Dbs],
    )(
      implicit monadF: Monad[F],
      flattener: Flattener[C0Dbs :: C1Dbs :: C2Dbs :: C3Dbs :: C4Dbs :: HNil, CDbs],
    ): ParentAssembler[F, A, ADb :: CDbs] =
      mkParent(
        par,
        Vector(
          eraseAssemblerType(c0Assembler),
          eraseAssemblerType(c1Assembler),
          eraseAssemblerType(c2Assembler),
          eraseAssemblerType(c3Assembler),
          eraseAssemblerType(c4Assembler),
        ),
        flattener,
        monadF,
      )

  }
}

private[doobieroll] object ToAssemblerSyntax {

  def seqToHList[HL <: HList](orig: Vector[Any]): HL = {

    @tailrec def impl(acc: HList, rest: Vector[Any]): HL =
      rest match {
        case Vector()  => acc.asInstanceOf[HL]
        case i +: rest => impl(i :: acc, rest)
      }

    // Reverse so we can use ::
    impl(HNil, orig.reverse)
  }

  def collectSuccess[F[_]](
    results: Vector[Vector[F[Any]]],
  )(implicit M: Monad[F]): F[Vector[Vector[Any]]] = results.map(_.sequence).sequence

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy