diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31ee2602..bf7af3eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: fail-fast: false matrix: java: ['adopt@1.8', 'adopt@1.11'] - scala: ['2.11.12', '2.12.14', '2.13.6', '3.0.2'] + scala: ['2.11.12', '2.12.14', '2.13.6', '3.1.0'] steps: - name: Checkout current branch uses: actions/checkout@v2.3.4 diff --git a/build.sbt b/build.sbt index c6bd8bce..fed90b35 100644 --- a/build.sbt +++ b/build.sbt @@ -23,7 +23,7 @@ inThisBuild( addCommandAlias("fmt", "all scalafmtSbt scalafmt test:scalafmt") addCommandAlias("check", "all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck") -val zioVersion = "2.0.0-M4" +val zioVersion = "2.0.0-M6-2" lazy val root = project .in(file(".")) diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index fa6e43d2..dacb1686 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -25,7 +25,7 @@ object BuildHelper { val Scala211: String = versions("2.11") val Scala212: String = versions("2.12") val Scala213: String = versions("2.13") - val ScalaDotty: String = versions("3.0") + val ScalaDotty: String = versions("3.1") def buildInfoSettings(packageName: String) = Seq( diff --git a/zio-query/shared/src/main/scala/zio/query/Cache.scala b/zio-query/shared/src/main/scala/zio/query/Cache.scala index 96a022a7..88c2bb3b 100644 --- a/zio-query/shared/src/main/scala/zio/query/Cache.scala +++ b/zio-query/shared/src/main/scala/zio/query/Cache.scala @@ -1,6 +1,6 @@ package zio.query -import zio.{ IO, Ref, UIO, ZTraceElement } +import zio.{ IO, Ref, UIO, ZIO, ZTraceElement } import zio.stacktracer.TracingImplicits.disableAutoTrace /** @@ -49,7 +49,7 @@ object Cache { * Constructs an empty cache. */ def empty(implicit trace: ZTraceElement): UIO[Cache] = - Ref.make(Map.empty[Any, Any]).map(new Default(_)) + ZIO.succeed(Cache.unsafeMake()) private final class Default(private val state: Ref[Map[Any, Any]]) extends Cache { @@ -75,4 +75,7 @@ object Cache { def remove[E, A](request: Request[E, A])(implicit trace: ZTraceElement): UIO[Unit] = state.update(_ - request) } + + private[query] def unsafeMake(): Cache = + new Default(Ref.unsafeMake(Map.empty[Any, Any])) } diff --git a/zio-query/shared/src/main/scala/zio/query/DataSource.scala b/zio-query/shared/src/main/scala/zio/query/DataSource.scala index adbd9645..22e6314b 100644 --- a/zio-query/shared/src/main/scala/zio/query/DataSource.scala +++ b/zio-query/shared/src/main/scala/zio/query/DataSource.scala @@ -1,6 +1,6 @@ package zio.query -import zio.{ Chunk, NeedsEnv, ZIO, ZTraceElement } +import zio.{ Chunk, NeedsEnv, ZEnvironment, ZIO, ZTraceElement } import zio.stacktracer.TracingImplicits.disableAutoTrace /** @@ -116,17 +116,35 @@ trait DataSource[-R, -A] { self => /** * Provides this data source with its required environment. */ - final def provide(r: Described[R])(implicit ev: NeedsEnv[R]): DataSource[Any, A] = - provideSome(Described(_ => r.value, s"_ => ${r.description}")) + @deprecated("use provideEnvironment", "2.0.0") + final def provide(r: Described[ZEnvironment[R]])(implicit ev: NeedsEnv[R]): DataSource[Any, A] = + provideEnvironment(r) + + /** + * Provides this data source with its required environment. + */ + final def provideEnvironment(r: Described[ZEnvironment[R]])(implicit ev: NeedsEnv[R]): DataSource[Any, A] = + provideSomeEnvironment(Described(_ => r.value, s"_ => ${r.description}")) + + /** + * Provides this data source with part of its required environment. + */ + @deprecated("use provideSomeEnvironment", "2.0.0") + final def provideSome[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R]): DataSource[R0, A] = + provideSomeEnvironment(f) /** * Provides this data source with part of its required environment. */ - final def provideSome[R0](f: Described[R0 => R])(implicit ev: NeedsEnv[R]): DataSource[R0, A] = + final def provideSomeEnvironment[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R]): DataSource[R0, A] = new DataSource[R0, A] { - val identifier = s"${self.identifier}.provideSome(${f.description})" + val identifier = s"${self.identifier}.provideSomeEnvironment(${f.description})" def runAll(requests: Chunk[Chunk[A]])(implicit trace: ZTraceElement): ZIO[R0, Nothing, CompletedRequestMap] = - self.runAll(requests).provideSome(f.value) + self.runAll(requests).provideSomeEnvironment(f.value) } /** diff --git a/zio-query/shared/src/main/scala/zio/query/ZQuery.scala b/zio-query/shared/src/main/scala/zio/query/ZQuery.scala index 278b2b05..525028aa 100644 --- a/zio-query/shared/src/main/scala/zio/query/ZQuery.scala +++ b/zio-query/shared/src/main/scala/zio/query/ZQuery.scala @@ -42,7 +42,7 @@ import scala.reflect.ClassTag * Concise Data Access" by Simon Marlow, Louis Brandy, Jonathan Coens, and Jon * Purdy. [[http://simonmar.github.io/bib/papers/haxl-icfp14.pdf]] */ -final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), Nothing, Result[R, E, A]]) { self => +final class ZQuery[-R, +E, +A] private (private val step: ZIO[R, Nothing, Result[R, E, A]]) { self => /** * Syntax for adding aspects. @@ -136,9 +136,8 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), */ def cached(implicit trace: ZTraceElement): ZQuery[R, E, A] = for { - queryContext <- ZQuery.queryContext - cachingEnabled <- ZQuery.fromZIO(queryContext.cachingEnabled.getAndSet(true)) - a <- self.ensuring(ZQuery.fromZIO(queryContext.cachingEnabled.set(cachingEnabled))) + cachingEnabled <- ZQuery.fromZIO(ZQuery.cachingEnabled.getAndSet(true)) + a <- self.ensuring(ZQuery.fromZIO(ZQuery.cachingEnabled.set(cachingEnabled))) } yield a /** @@ -338,12 +337,12 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), */ @deprecated("use mapQuery", "0.3.0") final def mapM[R1 <: R, E1 >: E, B](f: A => ZIO[R1, E1, B])(implicit trace: ZTraceElement): ZQuery[R1, E1, B] = - mapQuery(f) + mapZIO(f) /** * Maps the specified effectual function over the result of this query. */ - final def mapQuery[R1 <: R, E1 >: E, B](f: A => ZIO[R1, E1, B])(implicit trace: ZTraceElement): ZQuery[R1, E1, B] = + final def mapZIO[R1 <: R, E1 >: E, B](f: A => ZIO[R1, E1, B])(implicit trace: ZTraceElement): ZQuery[R1, E1, B] = flatMap(a => ZQuery.fromZIO(f(a))) /** @@ -374,47 +373,76 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), foldQuery(e => ZQuery.die(f(e)), a => ZQuery.succeed(a)) /** - * Provides this query with its required environment. + * Provides a layer to this query, which translates it to another level. */ - final def provide(r: => Described[R])(implicit ev: NeedsEnv[R], trace: ZTraceElement): ZQuery[Any, E, A] = - provideSome(Described(_ => r.value, s"_ => ${r.description}")) + final def provide[E1 >: E, R0]( + layer: => Described[ZLayer[R0, E1, R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): ZQuery[R0, E1, A] = + ZQuery { + layer.value.build.exit.use { + case Exit.Failure(e) => ZIO.succeedNow(Result.fail(e)) + case Exit.Success(r) => self.provideEnvironment(Described(r, layer.description)).step + } + } /** * Provides the part of the environment that is not part of the `ZEnv`, * leaving a query that only depends on the `ZEnv`. */ - final def provideCustomLayer[E1 >: E, R1 <: Has[_]]( + final def provideCustom[E1 >: E, R1]( layer: => Described[ZLayer[ZEnv, E1, R1]] )(implicit ev: ZEnv with R1 <:< R, tag: Tag[R1], trace: ZTraceElement): ZQuery[ZEnv, E1, A] = - provideSomeLayer(layer) + provideSome(layer) + + /** + * Provides the part of the environment that is not part of the `ZEnv`, + * leaving a query that only depends on the `ZEnv`. + */ + @deprecated("use provideCustom", "2.0.0") + final def provideCustomLayer[E1 >: E, R1]( + layer: => Described[ZLayer[ZEnv, E1, R1]] + )(implicit ev: ZEnv with R1 <:< R, tag: Tag[R1], trace: ZTraceElement): ZQuery[ZEnv, E1, A] = + provideCustom(layer) + + /** + * Provides this query with its required environment. + */ + final def provideEnvironment( + r: => Described[ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): ZQuery[Any, E, A] = + provideSomeEnvironment(Described(_ => r.value, s"_ => ${r.description}")) /** * Provides a layer to this query, which translates it to another level. */ - final def provideLayer[E1 >: E, R0, R1 <: Has[_]]( - layer: => Described[ZLayer[R0, E1, R1]] - )(implicit ev1: R1 <:< R, ev2: NeedsEnv[R], trace: ZTraceElement): ZQuery[R0, E1, A] = - ZQuery { - layer.value.build.provideSome[(R0, QueryContext)](_._1).exit.use { - case Exit.Failure(e) => ZIO.succeedNow(Result.fail(e)) - case Exit.Success(r) => self.provide(Described(r, layer.description)).step - } - } + @deprecated("use provide", "2.0.0") + final def provideLayer[E1 >: E, R0]( + layer: => Described[ZLayer[R0, E1, R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): ZQuery[R0, E1, A] = + provide(layer) + + /** + * Splits the environment into two parts, providing one part using the + * specified layer and leaving the remainder `R0`. + */ + final def provideSome[R0]: ZQuery.ProvideSome[R0, R, E, A] = + new ZQuery.ProvideSome(self) /** * Provides this query with part of its required environment. */ - final def provideSome[R0]( - f: => Described[R0 => R] + final def provideSomeEnvironment[R0]( + f: => Described[ZEnvironment[R0] => ZEnvironment[R]] )(implicit ev: NeedsEnv[R], trace: ZTraceElement): ZQuery[R0, E, A] = - ZQuery(step.map(_.provideSome(f)).provideSome(r => (f.value(r._1), r._2))) + ZQuery(step.map(_.provideSomeEnvironment(f)).provideSomeEnvironment((r => (f.value(r))))) /** * Splits the environment into two parts, providing one part using the * specified layer and leaving the remainder `R0`. */ - final def provideSomeLayer[R0 <: Has[_]]: ZQuery.ProvideSomeLayer[R0, R, E, A] = - new ZQuery.ProvideSomeLayer(self) + @deprecated("use provideSome", "2.0.0") + final def provideSomeLayer[R0]: ZQuery.ProvideSome[R0, R, E, A] = + provideSome /** * Races this query with the specified query, returning the result of the @@ -427,7 +455,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), def coordinate( exit: Exit[Nothing, Result[R1, E1, A1]], fiber: Fiber[Nothing, Result[R1, E1, A1]] - ): ZIO[(R1, QueryContext), Nothing, Result[R1, E1, A1]] = + ): ZIO[R1, Nothing, Result[R1, E1, A1]] = exit.foldZIO( cause => fiber.join.map(_.mapErrorCause(_ && cause)), { @@ -492,11 +520,18 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), * Returns an effect that models executing this query with the specified * cache. */ - final def runCache(cache: => Cache)(implicit trace: ZTraceElement): ZIO[R, E, A] = - for { - ref <- FiberRef.make(true) - a <- runContext(QueryContext(cache, ref)) - } yield a + final def runCache(cache: => Cache)(implicit trace: ZTraceElement): ZIO[R, E, A] = { + + def run(query: ZQuery[R, E, A]): ZIO[R, E, A] = + query.step.flatMap { + case Result.Blocked(br, Continue.Effect(c)) => br.run *> run(c) + case Result.Blocked(br, Continue.Get(io)) => br.run *> io + case Result.Done(a) => ZIO.succeedNow(a) + case Result.Fail(e) => ZIO.failCause(e) + } + + ZQuery.currentCache.locally(cache)(run(self)) + } /** * Returns an effect that models executing this query, returning the query @@ -526,7 +561,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), /** * Extracts a Some value into the value channel while moving the None into the error channel for easier composition * - * Inverse of [[ZQuery.collectSome]] + * Inverse of [[ZQuery.unoption]] */ def some[B](implicit ev: A IsSubtypeOfOutput Option[B], trace: ZTraceElement): ZQuery[R, Option[E], B] = self.foldQuery[R, Option[E], B]( @@ -570,14 +605,14 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), /** * Returns a new query that executes this one and times the execution. */ - final def timed(implicit trace: ZTraceElement): ZQuery[R with Has[Clock], E, (Duration, A)] = + final def timed(implicit trace: ZTraceElement): ZQuery[R with Clock, E, (Duration, A)] = summarized(Clock.nanoTime)((start, end) => Duration.fromNanos(end - start)) /** * Returns an effect that will timeout this query, returning `None` if the * timeout elapses before the query was completed. */ - final def timeout(duration: => Duration)(implicit trace: ZTraceElement): ZQuery[R with Has[Clock], E, Option[A]] = + final def timeout(duration: => Duration)(implicit trace: ZTraceElement): ZQuery[R with Clock, E, Option[A]] = timeoutTo(None)(Some(_))(duration) /** @@ -586,7 +621,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), */ final def timeoutFail[E1 >: E](e: => E1)(duration: => Duration)(implicit trace: ZTraceElement - ): ZQuery[R with Has[Clock], E1, A] = + ): ZQuery[R with Clock, E1, A] = timeoutTo(ZQuery.fail(e))(ZQuery.succeedNow)(duration).flatten /** @@ -595,7 +630,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), */ final def timeoutFailCause[E1 >: E](cause: => Cause[E1])(duration: => Duration)(implicit trace: ZTraceElement - ): ZQuery[R with Has[Clock], E1, A] = + ): ZQuery[R with Clock, E1, A] = timeoutTo(ZQuery.failCause(cause))(ZQuery.succeedNow)(duration).flatten /** @@ -605,7 +640,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), @deprecated("use timeoutFailCause", "0.3.0") final def timeoutHalt[E1 >: E](cause: => Cause[E1])(duration: => Duration)(implicit trace: ZTraceElement - ): ZQuery[R with Has[Clock], E1, A] = + ): ZQuery[R with Clock, E1, A] = timeoutFailCause(cause)(duration) /** @@ -621,9 +656,8 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), */ def uncached(implicit trace: ZTraceElement): ZQuery[R, E, A] = for { - queryContext <- ZQuery.queryContext - cachingEnabled <- ZQuery.fromZIO(queryContext.cachingEnabled.getAndSet(false)) - a <- self.ensuring(ZQuery.fromZIO(queryContext.cachingEnabled.set(cachingEnabled))) + cachingEnabled <- ZQuery.fromZIO(ZQuery.cachingEnabled.getAndSet(false)) + a <- self.ensuring(ZQuery.fromZIO(ZQuery.cachingEnabled.set(cachingEnabled))) } yield a /** @@ -669,7 +703,7 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), )(f: E => E1)(implicit trace: ZTraceElement): ZQuery[R, E1, A] = catchAllCause { cause => cause.find { - case Cause.Die(t) if pf.isDefinedAt(t) => pf(t) + case Cause.Die(t, _) if pf.isDefinedAt(t) => pf(t) }.fold(ZQuery.failCause(cause.map(f)))(ZQuery.fail(_)) } @@ -839,17 +873,6 @@ final class ZQuery[-R, +E, +A] private (private val step: ZIO[(R, QueryContext), case (_, Result.Fail(e)) => Result.fail(e) } } - - /** - * Returns an effect that models executing this query with the specified - * context. - */ - private[query] final def runContext(queryContext: QueryContext)(implicit trace: ZTraceElement): ZIO[R, E, A] = - step.provideSome[R]((_, queryContext)).flatMap { - case Result.Blocked(br, c) => br.run(queryContext.cache) *> c.runContext(queryContext) - case Result.Done(a) => ZIO.succeedNow(a) - case Result.Fail(e) => ZIO.failCause(e) - } } object ZQuery { @@ -863,21 +886,16 @@ object ZQuery { * val portNumber = effect.access(_.config.portNumber) * }}} */ - final def access[R]: AccessPartiallyApplied[R] = - new AccessPartiallyApplied[R] + @deprecated("use environmentWith", "2..0.0") + final def access[R]: EnvironmentWithPartiallyApplied[R] = + environmentWith /** * Effectfully accesses the environment of the effect. */ - @deprecated("use accessQuery", "0.3.0") - final def accessM[R]: AccessQueryPartiallyApplied[R] = - accessQuery - - /** - * Effectfully accesses the environment of the effect. - */ - final def accessQuery[R]: AccessQueryPartiallyApplied[R] = - new AccessQueryPartiallyApplied[R] + @deprecated("use environmentWithQuery", "0.3.0") + final def accessM[R]: EnvironmentWithQueryPartiallyApplied[R] = + environmentWithQuery /** * Collects a collection of queries into a query returning a collection of @@ -925,9 +943,30 @@ object ZQuery { /** * Accesses the whole environment of the query. */ - def environment[R](implicit trace: ZTraceElement): ZQuery[R, Nothing, R] = + def environment[R](implicit trace: ZTraceElement): ZQuery[R, Nothing, ZEnvironment[R]] = ZQuery.fromZIO(ZIO.environment) + /** + * Accesses the environment of the effect. + * {{{ + * val portNumber = effect.access(_.config.portNumber) + * }}} + */ + final def environmentWith[R]: EnvironmentWithPartiallyApplied[R] = + new EnvironmentWithPartiallyApplied[R] + + /** + * Effectfully accesses the environment of the effect. + */ + final def environmentWithQuery[R]: EnvironmentWithQueryPartiallyApplied[R] = + new EnvironmentWithQueryPartiallyApplied[R] + + /** + * Effectfully accesses the environment of the effect. + */ + final def environmentWithZIO[R]: EnvironmentWithZIOPartiallyApplied[R] = + new EnvironmentWithZIOPartiallyApplied[R] + /** * Constructs a query that fails with the specified error. */ @@ -1182,10 +1221,10 @@ object ZQuery { ZIO.suspendSucceed { val request = request0 val dataSource = dataSource0 - ZIO.environment[(R, QueryContext)].flatMap { case (_, queryContext) => - queryContext.cachingEnabled.get.flatMap { cachingEnabled => - if (cachingEnabled) { - queryContext.cache.lookup(request).flatMap { + ZQuery.cachingEnabled.get.flatMap { cachingEnabled => + if (cachingEnabled) { + ZQuery.currentCache.get.flatMap { cache => + cache.lookup(request).flatMap { case Left(ref) => UIO.succeedNow( Result.blocked( @@ -1199,13 +1238,13 @@ object ZQuery { case Some(b) => Result.fromEither(b) } } - } else { - Ref.make(Option.empty[Either[E, B]]).map { ref => - Result.blocked( - BlockedRequests.single(dataSource, BlockedRequest(request, ref)), - Continue(request, dataSource, ref) - ) - } + } + } else { + Ref.make(Option.empty[Either[E, B]]).map { ref => + Result.blocked( + BlockedRequests.single(dataSource, BlockedRequest(request, ref)), + Continue(request, dataSource, ref) + ) } } } @@ -1225,7 +1264,7 @@ object ZQuery { * Constructs a query from an effect. */ def fromZIO[R, E, A](effect: => ZIO[R, E, A])(implicit trace: ZTraceElement): ZQuery[R, E, A] = - ZQuery(ZIO.suspendSucceed(effect).foldCause(Result.fail, Result.done).provideSome(_._1)) + ZQuery(ZIO.suspendSucceed(effect).foldCause(Result.fail, Result.done)) /** * Constructs a query that fails with the specified cause. @@ -1296,6 +1335,33 @@ object ZQuery { )(implicit ev: CanFail[E], trace: ZTraceElement): ZQuery[R, Nothing, (Iterable[E], Iterable[B])] = ZQuery.foreachPar(as)(f(_).either).map(partitionMap(_)(identity)) + /** + * Accesses the whole environment of the query. + */ + def service[R: Tag: IsNotIntersection](implicit trace: ZTraceElement): ZQuery[R, Nothing, R] = + ZQuery.fromZIO(ZIO.service) + + /** + * Accesses the environment of the effect. + * {{{ + * val portNumber = effect.access(_.config.portNumber) + * }}} + */ + final def serviceWith[R]: ServiceWithPartiallyApplied[R] = + new ServiceWithPartiallyApplied[R] + + /** + * Effectfully accesses the environment of the effect. + */ + final def serviceWithQuery[R]: ServiceWithQueryPartiallyApplied[R] = + new ServiceWithQueryPartiallyApplied[R] + + /** + * Effectfully accesses the environment of the effect. + */ + final def serviceWithZIO[R]: ServiceWithZIOPartiallyApplied[R] = + new ServiceWithZIOPartiallyApplied[R] + /** * Constructs a query that succeeds with the optional value. */ @@ -1335,34 +1401,41 @@ object ZQuery { def unwrap[R, E, A](zio: => ZIO[R, E, ZQuery[R, E, A]])(implicit trace: ZTraceElement): ZQuery[R, E, A] = ZQuery.fromZIO(zio).flatten - final class AccessPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { - def apply[A](f: R => A)(implicit trace: ZTraceElement): ZQuery[R, Nothing, A] = + final class EnvironmentWithPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[A](f: ZEnvironment[R] => A)(implicit trace: ZTraceElement): ZQuery[R, Nothing, A] = environment[R].map(f) } - final class AccessQueryPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { - def apply[E, A](f: R => ZQuery[R, E, A])(implicit trace: ZTraceElement): ZQuery[R, E, A] = + final class EnvironmentWithQueryPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[E, A](f: ZEnvironment[R] => ZQuery[R, E, A])(implicit trace: ZTraceElement): ZQuery[R, E, A] = environment[R].flatMap(f) } - final class ProvideSomeLayer[R0 <: Has[_], -R, +E, +A](private val self: ZQuery[R, E, A]) extends AnyVal { - def apply[E1 >: E, R1 <: Has[_]]( + final class EnvironmentWithZIOPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[E, A](f: ZEnvironment[R] => ZIO[R, E, A])(implicit trace: ZTraceElement): ZQuery[R, E, A] = + environment[R].mapZIO(f) + } + + final class ProvideSome[R0, -R, +E, +A](private val self: ZQuery[R, E, A]) extends AnyVal { + def apply[E1 >: E, R1]( layer: => Described[ZLayer[R0, E1, R1]] )(implicit ev1: R0 with R1 <:< R, ev2: NeedsEnv[R], tag: Tag[R1], trace: ZTraceElement): ZQuery[R0, E1, A] = - self.provideLayer[E1, R0, R0 with R1](Described(ZLayer.environment[R0] ++ layer.value, layer.description)) + self + .asInstanceOf[ZQuery[R0 with R1, E, A]] + .provide(Described(ZLayer.environment[R0] ++ layer.value, layer.description)) } final class TimeoutTo[-R, +E, +A, +B](self: ZQuery[R, E, A], b: () => B) { def apply[B1 >: B]( f: A => B1 - )(duration: => Duration)(implicit trace: ZTraceElement): ZQuery[R with Has[Clock], E, B1] = - ZQuery.environment[Has[Clock]].flatMap { clock => + )(duration: => Duration)(implicit trace: ZTraceElement): ZQuery[R with Clock, E, B1] = + ZQuery.environment[Clock].flatMap { clock => def race( query: ZQuery[R, E, B1], fiber: Fiber[Nothing, B1] ): ZQuery[R, E, B1] = ZQuery { - query.step.raceWith[(R, QueryContext), Nothing, Nothing, B1, Result[R, E, B1]](fiber.join)( + query.step.raceWith[R, Nothing, Nothing, B1, Result[R, E, B1]](fiber.join)( (leftExit, rightFiber) => leftExit.foldZIO( cause => rightFiber.interrupt *> ZIO.succeedNow(Result.fail(cause)), @@ -1389,10 +1462,31 @@ object ZQuery { } } + final class ServiceWithPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[A]( + f: R => A + )(implicit ev: IsNotIntersection[R], tag: Tag[R], trace: ZTraceElement): ZQuery[R, Nothing, A] = + service[R].map(f) + } + + final class ServiceWithQueryPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[E, A]( + f: R => ZQuery[R, E, A] + )(implicit ev: IsNotIntersection[R], tag: Tag[R], race: ZTraceElement): ZQuery[R, E, A] = + service[R].flatMap(f) + } + + final class ServiceWithZIOPartiallyApplied[R](private val dummy: Boolean = true) extends AnyVal { + def apply[E, A]( + f: R => ZIO[R, E, A] + )(implicit ev: IsNotIntersection[R], tag: Tag[R], trace: ZTraceElement): ZQuery[R, E, A] = + service[R].mapZIO(f) + } + /** * Constructs a query from an effect that returns a result. */ - private def apply[R, E, A](step: ZIO[(R, QueryContext), Nothing, Result[R, E, A]]): ZQuery[R, E, A] = + private def apply[R, E, A](step: ZIO[R, Nothing, Result[R, E, A]]): ZQuery[R, E, A] = new ZQuery(step) /** @@ -1410,15 +1504,15 @@ object ZQuery { (bs.result(), cs.result()) } - /** - * Returns a query that accesses the context. - */ - private def queryContext(implicit trace: ZTraceElement): ZQuery[Any, Nothing, QueryContext] = - ZQuery(ZIO.access[(Any, QueryContext)] { case (_, queryContext) => Result.done(queryContext) }) - /** * Constructs a query that succeeds with the specified value. */ private def succeedNow[A](value: A): ZQuery[Any, Nothing, A] = ZQuery(ZIO.succeedNow(Result.done(value))) + + private[query] val cachingEnabled: FiberRef[Boolean] = + FiberRef.unsafeMake(true) + + private[query] val currentCache: FiberRef[Cache] = + FiberRef.unsafeMake(Cache.unsafeMake()) } diff --git a/zio-query/shared/src/main/scala/zio/query/internal/BlockedRequests.scala b/zio-query/shared/src/main/scala/zio/query/internal/BlockedRequests.scala index 8f6c2833..94b26c2c 100644 --- a/zio-query/shared/src/main/scala/zio/query/internal/BlockedRequests.scala +++ b/zio-query/shared/src/main/scala/zio/query/internal/BlockedRequests.scala @@ -2,9 +2,9 @@ package zio.query.internal import scala.annotation.tailrec -import zio.{ Ref, ZIO, ZTraceElement } +import zio.{ Ref, ZEnvironment, ZIO, ZTraceElement } import zio.query.internal.BlockedRequests._ -import zio.query.{ Cache, DataSource, DataSourceAspect, Described } +import zio.query.{ Cache, DataSource, DataSourceAspect, Described, ZQuery } import zio.stacktracer.TracingImplicits.disableAutoTrace /** @@ -46,20 +46,27 @@ private[query] sealed trait BlockedRequests[-R] { self => /** * Provides each data source with part of its required environment. */ - final def provideSome[R0](f: Described[R0 => R]): BlockedRequests[R0] = + @deprecated("use provideSomeEnvironment", "2.0.0") + final def provideSome[R0](f: Described[ZEnvironment[R0] => ZEnvironment[R]]): BlockedRequests[R0] = + provideSomeEnvironment(f) + + /** + * Provides each data source with part of its required environment. + */ + final def provideSomeEnvironment[R0](f: Described[ZEnvironment[R0] => ZEnvironment[R]]): BlockedRequests[R0] = self match { case Empty => Empty - case Both(l, r) => Both(l.provideSome(f), r.provideSome(f)) - case Then(l, r) => Then(l.provideSome(f), r.provideSome(f)) - case Single(ds, br) => Single(ds.provideSome(f), br) + case Both(l, r) => Both(l.provideSomeEnvironment(f), r.provideSomeEnvironment(f)) + case Then(l, r) => Then(l.provideSomeEnvironment(f), r.provideSomeEnvironment(f)) + case Single(ds, br) => Single(ds.provideSomeEnvironment(f), br) } /** * Executes all requests, submitting requests to each data source in * parallel. */ - def run(cache: Cache)(implicit trace: ZTraceElement): ZIO[R, Nothing, Unit] = - ZIO.suspendSucceed { + def run(implicit trace: ZTraceElement): ZIO[R, Nothing, Unit] = + ZQuery.currentCache.get.flatMap { cache => ZIO.foreachDiscard(BlockedRequests.flatten(self)) { requestsByDataSource => ZIO.foreachParDiscard(requestsByDataSource.toIterable) { case (dataSource, sequential) => for { diff --git a/zio-query/shared/src/main/scala/zio/query/internal/Continue.scala b/zio-query/shared/src/main/scala/zio/query/internal/Continue.scala index cae48130..b4c4b7e0 100644 --- a/zio-query/shared/src/main/scala/zio/query/internal/Continue.scala +++ b/zio-query/shared/src/main/scala/zio/query/internal/Continue.scala @@ -3,7 +3,7 @@ package zio.query.internal import zio.query._ import zio.query.internal.Continue._ import zio.stacktracer.TracingImplicits.disableAutoTrace -import zio.{ CanFail, Cause, IO, NeedsEnv, Ref, ZIO, ZTraceElement } +import zio.{ CanFail, Cause, IO, NeedsEnv, Ref, ZEnvironment, ZIO, ZTraceElement } /** * A `Continue[R, E, A]` models a continuation of a blocked request that @@ -90,19 +90,21 @@ private[query] sealed trait Continue[-R, +E, +A] { self => /** * Purely contramaps over the environment type of this continuation. */ - final def provideSome[R0](f: Described[R0 => R])(implicit ev: NeedsEnv[R], trace: ZTraceElement): Continue[R0, E, A] = - self match { - case Effect(query) => effect(query.provideSome(f)) - case Get(io) => get(io) - } + @deprecated("use provideSomeEnvironment", "2.0.0") + final def provideSome[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): Continue[R0, E, A] = + provideSomeEnvironment(f) /** - * Runs this continuation. + * Purely contramaps over the environment type of this continuation. */ - final def runContext(queryContext: QueryContext)(implicit trace: ZTraceElement): ZIO[R, E, A] = + final def provideSomeEnvironment[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): Continue[R0, E, A] = self match { - case Effect(query) => query.runContext(queryContext) - case Get(io) => io + case Effect(query) => effect(query.provideSomeEnvironment(f)) + case Get(io) => get(io) } /** diff --git a/zio-query/shared/src/main/scala/zio/query/internal/QueryContext.scala b/zio-query/shared/src/main/scala/zio/query/internal/QueryContext.scala deleted file mode 100644 index f563822e..00000000 --- a/zio-query/shared/src/main/scala/zio/query/internal/QueryContext.scala +++ /dev/null @@ -1,13 +0,0 @@ -package zio.query.internal - -import zio.query.Cache -import zio.stacktracer.TracingImplicits.disableAutoTrace -import zio.FiberRef - -/** - * `QueryContext` maintains the context of a query. Currently `QueryContext` - * simply maintains a `Cache` of requests and results but this will be - * augmented with other functionality such as logging and metrics in the - * future. - */ -private[query] final case class QueryContext(cache: Cache, cachingEnabled: FiberRef[Boolean]) diff --git a/zio-query/shared/src/main/scala/zio/query/internal/Result.scala b/zio-query/shared/src/main/scala/zio/query/internal/Result.scala index 503dd378..097356f2 100644 --- a/zio-query/shared/src/main/scala/zio/query/internal/Result.scala +++ b/zio-query/shared/src/main/scala/zio/query/internal/Result.scala @@ -3,7 +3,7 @@ package zio.query.internal import zio.query.internal.Result._ import zio.query.{ DataSourceAspect, Described } import zio.stacktracer.TracingImplicits.disableAutoTrace -import zio.{ CanFail, Cause, Exit, NeedsEnv, ZTraceElement } +import zio.{ CanFail, Cause, Exit, NeedsEnv, ZEnvironment, ZTraceElement } /** * A `Result[R, E, A]` is the result of running one step of a `ZQuery`. A @@ -68,15 +68,35 @@ private[query] sealed trait Result[-R, +E, +A] { self => /** * Provides this result with its required environment. */ - final def provide(r: Described[R])(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[Any, E, A] = - provideSome(Described(_ => r.value, s"_ => ${r.description}")) + @deprecated("use provideEnvironment", "2.0.0") + final def provide(r: Described[ZEnvironment[R]])(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[Any, E, A] = + provideEnvironment(r) + + /** + * Provides this result with its required environment. + */ + final def provideEnvironment( + r: Described[ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[Any, E, A] = + provideSomeEnvironment(Described(_ => r.value, s"_ => ${r.description}")) + + /** + * Provides this result with part of its required environment. + */ + @deprecated("use provideSomeEnvironment", "2.0.0") + final def provideSome[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[R0, E, A] = + provideSomeEnvironment(f) /** * Provides this result with part of its required environment. */ - final def provideSome[R0](f: Described[R0 => R])(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[R0, E, A] = + final def provideSomeEnvironment[R0]( + f: Described[ZEnvironment[R0] => ZEnvironment[R]] + )(implicit ev: NeedsEnv[R], trace: ZTraceElement): Result[R0, E, A] = self match { - case Blocked(br, c) => blocked(br.provideSome(f), c.provideSome(f)) + case Blocked(br, c) => blocked(br.provideSomeEnvironment(f), c.provideSomeEnvironment(f)) case Done(a) => done(a) case Fail(e) => fail(e) } diff --git a/zio-query/shared/src/test/scala/zio/query/ZQuerySpec.scala b/zio-query/shared/src/test/scala/zio/query/ZQuerySpec.scala index b106bf11..d52ed350 100644 --- a/zio-query/shared/src/test/scala/zio/query/ZQuerySpec.scala +++ b/zio-query/shared/src/test/scala/zio/query/ZQuerySpec.scala @@ -5,7 +5,7 @@ import zio.query.DataSourceAspect._ import zio.test.Assertion._ import zio.test.TestAspect.{ after, nonFlaky, silent } import zio.test._ -import zio.test.environment.{ TestClock, TestConsole, TestEnvironment } +import zio.test.{ TestClock, TestConsole, TestEnvironment } object ZQuerySpec extends ZIOBaseSpec { @@ -88,7 +88,7 @@ object ZQuerySpec extends ZIOBaseSpec { val query = Cache.getAll *> Cache.put(0, 1) *> Cache.getAll assertM(query.uncached.run)(equalTo(Map(0 -> 1))) } @@ nonFlaky - ).provideCustomLayer(Cache.live), + ).provideCustom(Cache.live), suite("zipBatched")( test("queries to multiple data sources can be executed in parallel") { for { @@ -281,8 +281,8 @@ object ZQuerySpec extends ZIOBaseSpec { case object GetAllIds extends UserRequest[List[Int]] final case class GetNameById(id: Int) extends UserRequest[String] - val UserRequestDataSource: DataSource[Has[Console], UserRequest[Any]] = - DataSource.Batched.make[Has[Console], UserRequest[Any]]("UserRequestDataSource") { requests => + val UserRequestDataSource: DataSource[Console, UserRequest[Any]] = + DataSource.Batched.make[Console, UserRequest[Any]]("UserRequestDataSource") { requests => ZIO.when(requests.toSet.size != requests.size)(ZIO.dieMessage("Duplicate requests)")) *> Console.printLine(requests.toString).orDie *> ZIO.succeed { @@ -294,25 +294,25 @@ object ZQuerySpec extends ZIOBaseSpec { } } - val getAllUserIds: ZQuery[Has[Console], Nothing, List[Int]] = + val getAllUserIds: ZQuery[Console, Nothing, List[Int]] = ZQuery.fromRequest(GetAllIds)(UserRequestDataSource) - def getUserNameById(id: Int): ZQuery[Has[Console], Nothing, String] = + def getUserNameById(id: Int): ZQuery[Console, Nothing, String] = ZQuery.fromRequest(GetNameById(id))(UserRequestDataSource) - val getAllUserNames: ZQuery[Has[Console], Nothing, List[String]] = + val getAllUserNames: ZQuery[Console, Nothing, List[String]] = for { userIds <- getAllUserIds userNames <- ZQuery.foreachPar(userIds)(getUserNameById) } yield userNames case object GetFoo extends Request[Nothing, String] - val getFoo: ZQuery[Has[Console], Nothing, String] = ZQuery.fromRequest(GetFoo)( + val getFoo: ZQuery[Console, Nothing, String] = ZQuery.fromRequest(GetFoo)( DataSource.fromFunctionZIO("foo")(_ => Console.printLine("Running foo query") *> ZIO.succeed("foo")) ) case object GetBar extends Request[Nothing, String] - val getBar: ZQuery[Has[Console], Nothing, String] = ZQuery.fromRequest(GetBar)( + val getBar: ZQuery[Console, Nothing, String] = ZQuery.fromRequest(GetBar)( DataSource.fromFunctionZIO("bar")(_ => Console.printLine("Running bar query") *> ZIO.succeed("bar")) ) @@ -343,7 +343,7 @@ object ZQuerySpec extends ZIOBaseSpec { case object GetAll extends CacheRequest[Map[Int, Int]] final case class Put(key: Int, value: Int) extends CacheRequest[Unit] - type Cache = Has[Cache.Service] + type Cache = Cache.Service object Cache { @@ -447,12 +447,12 @@ object ZQuerySpec extends ZIOBaseSpec { 4 -> "d" ) - def backendGetAll: ZIO[Has[Console], Nothing, Map[Int, String]] = + def backendGetAll: ZIO[Console, Nothing, Map[Int, String]] = for { _ <- Console.printLine("getAll called").orDie } yield testData - def backendGetSome(ids: Chunk[Int]): ZIO[Has[Console], Nothing, Map[Int, String]] = + def backendGetSome(ids: Chunk[Int]): ZIO[Console, Nothing, Map[Int, String]] = for { _ <- Console.printLine(s"getSome ${ids.mkString(", ")} called").orDie } yield ids.flatMap { id => @@ -468,10 +468,10 @@ object ZQuerySpec extends ZIOBaseSpec { final case class Get(id: Int) extends Req[String] } - val ds: DataSource.Batched[Has[Console], Req[_]] = new DataSource.Batched[Has[Console], Req[_]] { + val ds: DataSource.Batched[Console, Req[_]] = new DataSource.Batched[Console, Req[_]] { override def run( requests: Chunk[Req[_]] - )(implicit trace: ZTraceElement): ZIO[Has[Console], Nothing, CompletedRequestMap] = { + )(implicit trace: ZTraceElement): ZIO[Console, Nothing, CompletedRequestMap] = { val (all, oneByOne) = requests.partition { case Req.GetAll => true case Req.Get(_) => false @@ -505,8 +505,8 @@ object ZQuerySpec extends ZIOBaseSpec { override val identifier: String = "test" } - def getAll: ZQuery[Has[Console], DataSourceErrors, Map[Int, String]] = + def getAll: ZQuery[Console, DataSourceErrors, Map[Int, String]] = ZQuery.fromRequest(Req.GetAll)(ds) - def get(id: Int): ZQuery[Has[Console], DataSourceErrors, String] = + def get(id: Int): ZQuery[Console, DataSourceErrors, String] = ZQuery.fromRequest(Req.Get(id))(ds) }