Skip to content

Instantly share code, notes, and snippets.

/JResource.scala

Created Dec 16, 2016
Embed
What would you like to do?
package jresource
import cats.data.Xor
import scala.util.control.NonFatal
object JResource {
type FinalActions = List[() => Unit]
final case class FinalActionsExecutionThrew(throwables: Seq[Throwable]) extends Exception
sealed abstract class ManagedResource[R] {
def map[B](f: (R) => B): ManagedResource[B] = {
new MappedManagedResource[R, B](this, f)
}
def flatMap[B](f: R => ManagedResource[B]): ManagedResource[B]
def acquireAndGet[B](f: R => B): B
protected def openAndReturnWithFinalActions(previousFinalActions: FinalActions): (R, FinalActions)
}
class DefaultManagedResource[R: Resource](resource: R) extends ManagedResource[R] {
override def map[B](f: (R) => B): ManagedResource[B] = {
new MappedManagedResource[R, B](this, f)
}
override def flatMap[B](f: (R) => ManagedResource[B]): ManagedResource[B] = {
new SequencedManagedResource[R, B](this, f)
}
override def acquireAndGet[B](f: (R) => B): B = {
try {
implicitly[Resource[R]].open(resource)
f(resource)
} finally {
implicitly[Resource[R]].close(resource)
}
}
override protected def openAndReturnWithFinalActions(previousFinalActions: FinalActions): (R, FinalActions) = {
val newFinalActions = (() => implicitly[Resource[R]].close(resource)) :: previousFinalActions
try {
implicitly[Resource[R]].open(resource)
(resource, newFinalActions)
} catch {
case NonFatal(nonFatal) =>
executeFinalActions(newFinalActions)
throw nonFatal
}
}
}
class MappedManagedResource[R: Resource, B](managedResource: ManagedResource[R], f: R => B) extends ManagedResource[B] {
override def map[C](g: (B) => C): ManagedResource[C] = {
new MappedManagedResource[R, C](managedResource, f andThen g)
}
override def flatMap[C](g: (B) => ManagedResource[C]): ManagedResource[C] = {
new SequencedManagedResource[B, C](this, r => g(f(r)))
}
override def acquireAndGet[C](g: (B) => C): C = {
managedResource.acquireAndGet(f andThen g)
}
override protected def openAndReturnWithFinalActions(previousFinalActions: FinalActions): (B, FinalActions) = {
val (r, finalActions) = managedResource.openAndReturnWithFinalActions(previousFinalActions)
try {
(f(r), finalActions)
} catch {
case NonFatal(nonFatal) =>
executeFinalActions(finalActions)
throw nonFatal
}
}
}
class SequencedManagedResource[R, C](managedResource: ManagedResource[R], f: R => ManagedResource[C]) extends ManagedResource[C] {
override def flatMap[D](g: (C) => ManagedResource[D]): ManagedResource[D] = {
new SequencedManagedResource[C, D](this, g)
}
override def acquireAndGet[D](g: (C) => D): D = {
val (b, bClosingActions) = managedResource.openAndReturnWithFinalActions(List.empty)
val (c, cClosingActions) = try {
f(b).openAndReturnWithFinalActions(bClosingActions)
} catch {
case NonFatal(nonFatal) =>
executeFinalActions(bClosingActions)
throw nonFatal
}
try {
g(f(c))
} finally {
executeFinalActions(cClosingActions)
}
}
override protected def openAndReturnWithFinalActions(previousFinalActions: FinalActions): (C, FinalActions) = {
val (b, bClosingActions) = managedResource.openAndReturnWithFinalActions(List.empty)
try {
f(b).openAndReturnWithFinalActions(bClosingActions)
} catch {
case NonFatal(nonFatal) =>
bClosingActions.foreach(action => action())
throw nonFatal
}
}
}
private def executeFinalActions(finalActions: FinalActions): Unit = {
val thrownThrowables = finalActions.map(action => Xor.catchNonFatal(action())).collect { case Xor.Left(throwable) => throwable }
if (thrownThrowables.nonEmpty) {
throw FinalActionsExecutionThrew(thrownThrowables)
}
}
trait Resource[R] {
def open(r: R): Unit = {
()
}
def close(r: R): Unit
}
type HasCloseMethod = {def close(): Unit}
implicit def hasCloseMethodToResource[HCM <: HasCloseMethod]: Resource[HCM] = {
new Resource[HCM] {
override def close(r: HCM): Unit = {
r.close()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment