Created
December 16, 2016 10:56
-
-
Save anonymous/32e847129948b84e622e715cecd4ff6b to your computer and use it in GitHub Desktop.
This file contains 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 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