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

org.opentorah.util.Effects.scala Maven / Gradle / Ivy

The newest version!
package org.opentorah.util

import zio.{Runtime, Task, ZIO}

object Effects:

  class Error(message: String, cause: Throwable) extends Throwable(message, cause):
    def this(message: String) = this(message, null)
    def this(cause: Throwable) = this(null, cause)

  private def throwable2error[R, A](zio: ZIO[R, Throwable, A]): ZIO[R, Error, A] = zio.mapError {
    case error: Error => error
    case error => new Error(error)
  }

  type IO[A] = zio.IO[Error, A]

  val ok: IO[Unit] = ZIO.succeed(())

  def fail(message: String): IO[Nothing] = ZIO.fail(new Error(message))

  def check(condition: Boolean, message: => String): IO[Unit] =
    if condition then ok else fail(message)

  // Note: In the past, when running with 1 cpu (on Cloud Run or in local Docker),
  // I had to use blockingRuntime to avoid deadlocks with nested unsafeRun() calls;
  // this seems to be no longer necessary.
  // TODO how am I supposed to do unsafeRun?
  // The following is from https://www.reddit.com/r/scala/comments/vjrvux/zio_20_released/
  // from ZIO Test documentation:
  //Unsafe.unsafe { implicit unsafe =>
  //  Runtime.default.unsafe.run(
  //    Random.nextIntBounded(10)
  //  ).getOrThrowFiberFailure()
  //}
  implicit class RuntimeExtension(thiz: Runtime[Any]) extends AnyVal:
    def unsafeRun[E, A](prog: zio.IO[E, A]): A =
      zio.Unsafe.unsafe(implicit _ => thiz.unsafe.run(prog).getOrThrowFiberFailure())

  def unsafeRun[E <: Throwable, A](io: => zio.IO[E, A]): A = Runtime.default.unsafeRun(io)

  def effect[A](f: => A): IO[A] = throwable2error(ZIO.attemptBlocking(f))

  def collectAll[R, A](zios: Seq[ZIO[R, Error, A]]): ZIO[R, Error, Seq[A]] = for
    runs: Seq[Either[Error, A]] <- ZIO.foreach(zios)(_.either)
    errors: Seq[Error] = runs.flatMap(_.left.toOption)
    results: Seq[A] = runs.flatMap(_.toOption)
    _ <- check(errors.isEmpty, errors.mkString("Errors:\n  ", "\n  ", "\n."))
  yield results

  def mapValues[R, A, B, C](map: Map[A, B])(f: B => ZIO[R, Error, C]): ZIO[R, Error, Map[A, C]] =
    collectAll(map.toSeq.map((a, b) => f(b).map(a -> _))).map(_.toMap)

  // TODO is this something from ZIO already?
  def required[R, A](optional: ZIO[R, Error, Option[A]], what: AnyRef): ZIO[R, Error, A] = for
    result <- optional
    _ <- check(result.isDefined, s"Required $what is missing")
  yield result.get




© 2015 - 2024 Weber Informatics LLC | Privacy Policy