Given:
EitherT[F[_], A, B]
encapsulates
F[Either[A, B]]
FutureT[F[_], A]
encapsulates
F[Future[A]]
Reader[A, B]
is simply A => B
Let:
Service[A]
is
EitherT[FutureT[Reader[Connection, A]], Error, A]
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.Statement
import scala.concurrent.Future
import scala.concurrent.{ ExecutionContext => EC }
import scala.util.Failure
import scala.util.Success
import scala.util.Try
object Service {
def transact[A]( s: Service[A]
, c: Connection
)(implicit ec: EC): Future[Try[A]] =
{
c synchronized val ea =
.run(c) map {
scase s@Success(_) =>
.commit()
c
scase f@Failure(_) =>
.rollback()
c
f}
ea}
def apply[A](a: A)(implicit ec: EC): Service[A] =
Service(_ => Future(Success(a)))
def apply[A](k: Connection => A)(implicit ec: EC): Service[A] =
Service(c => Future(Try(k(c))))
def insert( q: String
, k: PreparedStatement => Unit
)(implicit ec: EC): Service[Option[Int]] =
Service { c =>
Future {
{
Try val s = c.prepareStatement(q, Statement.RETURN_GENERATED_KEYS)
k(s)
.executeUpdate()
svar id: Option[Int] = None
val r = s.getGeneratedKeys()
if (r.next()) {
= Option(r.getInt(1))
id }
.close()
r.close()
s
id}
}
}
def update(u: String)(implicit ec: EC): Service[Unit] =
{ c =>
apply val s = c.createStatement()
.executeUpdate(u)
s.close()
s}
def update(q: String, k: PreparedStatement => Unit)(implicit ec: EC): Service[Unit] =
{ c =>
apply val s = c.prepareStatement(q)
k(s)
.executeUpdate()
s.close()
s}
def query[A](q: String, k: PreparedStatement => A)(implicit ec: EC): Service[A] =
{ c =>
apply val s = c.prepareStatement(q)
val a = k(s)
.close()
s
a}
}
case class Service[A](run: Connection => Future[Try[A]]) {
def map[B](f: A => B)(implicit ec: EC): Service[B] =
Service { c => run(c) map { _ map f } }
def mapFailure(f: Throwable => Throwable)(implicit ec: EC): Service[A] =
Service { c =>
run(c) map {
case Failure(t) => Failure(f(t))
case s@Success(_) => s
}
}
def flatMap[B](f: A => Service[B])(implicit ec: EC): Service[B] =
Service { c =>
run(c) flatMap {
case Failure(t) => Future(Failure(t))
case Success(a) => f(a).run(c)
}
}
def >>[B](x: Service[B])(implicit ec: EC): Service[B] =
{ _ => x }
flatMap
}