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

zio.query.internal.Continue.scala Maven / Gradle / Ivy

/*
 * Copyright 2019-2023 John A. De Goes and the ZIO Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package zio.query.internal

import zio._
import zio.query._
import zio.query.internal.Continue._
import zio.stacktracer.TracingImplicits.disableAutoTrace

/**
 * A `Continue[R, E, A]` models a continuation of a blocked request that
 * requires an environment `R` and may either fail with an `E` or succeed with
 * an `A`. A continuation may either be a `Get` that merely gets the result of a
 * blocked request (potentially transforming it with pure functions) or an
 * `Effect` that may perform arbitrary effects. This is used by the library
 * internally to determine whether it is safe to pipeline two requests that must
 * be executed sequentially.
 */
private[query] sealed trait Continue[-R, +E, +A] { self =>

  /**
   * Purely folds over the failure and success types of this continuation.
   */
  final def fold[B](failure: E => B, success: A => B)(implicit
    ev: CanFail[E],
    trace: Trace
  ): Continue[R, Nothing, B] =
    self match {
      case Effect(query) => Effect(query.fold(failure, success))
      case Get(io)       => Get(io.foldZIO(e => Exit.succeed(failure(e)), a => Exit.succeed(success(a))))
    }

  /**
   * Effectually folds over the failure and success types of this continuation.
   */
  final def foldCauseQuery[R1 <: R, E1, B](
    failure: Cause[E] => ZQuery[R1, E1, B],
    success: A => ZQuery[R1, E1, B]
  )(implicit trace: Trace): Continue[R1, E1, B] =
    self match {
      case Effect(query) => Effect(query.foldCauseQuery(failure, success))
      case Get(io)       => Effect(ZQuery.fromZIONow(io).foldCauseQuery(failure, success))
    }

  final def foldCauseZIO[R1 <: R, E1, B](
    failure: Cause[E] => ZIO[R1, E1, B],
    success: A => ZIO[R1, E1, B]
  )(implicit trace: Trace): Continue[R1, E1, B] =
    self match {
      case Effect(query) => Effect(query.foldCauseZIO(failure, success))
      case Get(io)       => Get(io.foldCauseZIO(failure, success))
    }

  final def foldZIO[R1 <: R, E1, B](
    failure: E => ZIO[R1, E1, B],
    success: A => ZIO[R1, E1, B]
  )(implicit trace: Trace): Continue[R1, E1, B] =
    foldCauseZIO(_.failureOrCause.fold(failure, Exit.failCause), success)

  /**
   * Purely maps over the success type of this continuation.
   */
  final def map[B](f: A => B)(implicit trace: Trace): Continue[R, E, B] =
    self match {
      case Effect(query) => Effect(query.map(f))
      case Get(io)       => Get(io.map(f))
    }

  final def mapBothCause[E1, B](failure: Cause[E] => Cause[E1], success: A => B)(implicit
    ev: CanFail[E],
    trace: Trace
  ): Continue[R, E1, B] =
    self match {
      case Effect(query) => Effect(query.mapBothCause(failure, success))
      case Get(io)       => Get(io.foldCauseZIO(e => Exit.failCause(failure(e)), a => Exit.succeed(success(a))))
    }

  /**
   * Transforms all data sources with the specified data source aspect.
   */
  final def mapDataSources[R1 <: R](f: DataSourceAspect[R1])(implicit trace: Trace): Continue[R1, E, A] =
    self match {
      case Effect(query) => Effect(query.mapDataSources(f))
      case Get(io)       => Get(io)
    }

  /**
   * Purely maps over the failure type of this continuation.
   */
  final def mapError[E1](f: E => E1)(implicit ev: CanFail[E], trace: Trace): Continue[R, E1, A] =
    self match {
      case Effect(query) => Effect(query.mapError(f))
      case Get(io)       => Get(io.mapError(f))
    }

  /**
   * Purely maps over the failure cause of this continuation.
   */
  final def mapErrorCause[E1](f: Cause[E] => Cause[E1])(implicit trace: Trace): Continue[R, E1, A] =
    self match {
      case Effect(query) => Effect(query.mapErrorCause(f))
      case Get(io)       => Get(io.mapErrorCause(f))
    }

  /**
   * Effectually maps over the success type of this continuation.
   */
  final def mapQuery[R1 <: R, E1 >: E, B](
    f: A => ZQuery[R1, E1, B]
  )(implicit trace: Trace): Continue[R1, E1, B] =
    self match {
      case Effect(query) => Effect(query.flatMap(f))
      case Get(io)       => Effect(ZQuery.fromZIONow(io).flatMap(f))
    }

  /**
   * Effectually maps over the success type of this continuation.
   */
  final def mapZIO[R1 <: R, E1 >: E, B](
    f: A => ZIO[R1, E1, B]
  )(implicit trace: Trace): Continue[R1, E1, B] =
    self match {
      case Effect(query) => Effect(query.mapZIO(f))
      case Get(io)       => Get(io.flatMap(f))
    }

  /**
   * Purely contramaps over the environment type of this continuation.
   */
  final def provideSomeEnvironment[R0](
    f: Described[ZEnvironment[R0] => ZEnvironment[R]]
  )(implicit trace: Trace): Continue[R0, E, A] =
    self match {
      case Effect(query) => Effect(query.provideSomeEnvironment(f))
      case Get(io)       => Get(io.provideSomeEnvironment(f.value))
    }

  /**
   * Combines this continuation with that continuation using the specified
   * function, in sequence.
   */
  final def zipWith[R1 <: R, E1 >: E, B, C](
    that: Continue[R1, E1, B]
  )(f: (A, B) => C)(implicit trace: Trace): Continue[R1, E1, C] =
    (self, that) match {
      case (Effect(l), Effect(r)) => Effect(l.zipWith(r)(f))
      case (Effect(l), Get(r))    => Effect(l.zipWith(ZQuery.fromZIONow(r))(f))
      case (Get(l), Effect(r))    => Effect(ZQuery.fromZIONow(l).zipWith(r)(f))
      case (Get(l), Get(r))       => Get(l.zipWith(r)(f))
    }

  /**
   * Combines this continuation with that continuation using the specified
   * function, in parallel.
   */
  final def zipWithPar[R1 <: R, E1 >: E, B, C](
    that: Continue[R1, E1, B]
  )(f: (A, B) => C)(implicit trace: Trace): Continue[R1, E1, C] =
    (self, that) match {
      case (Effect(l), Effect(r)) => Effect(l.zipWithPar(r)(f))
      case (Effect(l), Get(r))    => Effect(l.zipWith(ZQuery.fromZIONow(r))(f))
      case (Get(l), Effect(r))    => Effect(ZQuery.fromZIONow(l).zipWith(r)(f))
      case (Get(l), Get(r))       => Get(l.zipWith(r)(f))
    }

  /**
   * Combines this continuation with that continuation using the specified
   * function, batching requests to data sources.
   */
  final def zipWithBatched[R1 <: R, E1 >: E, B, C](
    that: Continue[R1, E1, B]
  )(f: (A, B) => C)(implicit trace: Trace): Continue[R1, E1, C] =
    (self, that) match {
      case (Effect(l), Effect(r)) => Effect(l.zipWithBatched(r)(f))
      case (Effect(l), Get(r))    => Effect(l.zipWith(ZQuery.fromZIONow(r))(f))
      case (Get(l), Effect(r))    => Effect(ZQuery.fromZIONow(l).zipWith(r)(f))
      case (Get(l), Get(r))       => Get(l.zipWith(r)(f))
    }
}

private[query] object Continue {

  /**
   * Constructs a continuation from a request, a data source, and a `Promise`
   * that will contain the result of the request when it is executed.
   */
  def apply[E, A](promise: Promise[E, A])(implicit trace: Trace): Continue[Any, E, A] =
    Get(promise.await)

  /**
   * Constructs a continuation that may perform arbitrary effects.
   */
  def effect[R, E, A](query: ZQuery[R, E, A]): Continue[R, E, A] =
    Effect(query)

  /**
   * Constructs a continuation that merely gets the result of a blocked request
   * (potentially transforming it with pure functions).
   */
  def get[R, E, A](io: ZIO[R, E, A]): Continue[R, E, A] =
    Get(io)

  final case class Effect[R, E, A](query: ZQuery[R, E, A]) extends Continue[R, E, A]
  final case class Get[R, E, A](io: ZIO[R, E, A])          extends Continue[R, E, A]
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy