// ets_tracing: off // port of: https://github.com/zio/zio-query/blob/5746d54dfbed8e3c35415355b09c8e6a54c49889/zio-query/shared/src/main/scala/zio/query/internal/Continue.scala import type { IO } from "@effect-ts/core/Effect" import * as T from "@effect-ts/core/Effect" import { _A, _E, _R } from "@effect-ts/core/Effect" import type * as E from "@effect-ts/core/Either" import { pipe } from "@effect-ts/core/Function" import * as O from "@effect-ts/core/Option" import type { _A as _GetA, _E as _GetE } from "@effect-ts/core/Utils" import type * as C from "@effect-ts/system/Cause" import * as REF from "@effect-ts/system/Ref" import type { Cache } from "../../Cache/index.js" import type { DataSource } from "../../DataSource/index.js" import type { DataSourceAspect } from "../../DataSourceAspect/index.js" import * as Q from "../../Query/index.js" import { QueryFailure } from "../../QueryFailure/index.js" import type { Request } from "../../Request/index.js" class Effect { readonly _tag = "Effect"; readonly [_R]!: (r: R) => never; readonly [_E]!: () => E; readonly [_A]!: () => A constructor(public readonly query: Q.Query) {} } class Get { readonly _tag = "Get"; readonly [_E]!: () => E; readonly [_A]!: () => A constructor(public readonly io: IO) {} } /** * 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. */ export type Continue = Effect | Get /** * Constructs a continuation from a request, a data source, and a `Ref` that * will contain the result of the request when it is executed. */ export function apply>( request: A, dataSource: DataSource, ref: REF.Ref, _GetA>>> ): Continue, _GetA> { return pipe( REF.get(ref), T.chain((v) => O.fold_( v, () => T.die(new QueryFailure(dataSource, request)), (b) => T.fromEither(() => b) ) ), get ) } /** * Constructs a continuation that may perform arbitrary effects. */ export function effect(query: Q.Query): Continue { return new Effect(query) } /** * Constructs a continuation that merely gets the result of a blocked request * (potentially transforming it with pure functions). */ export function get(io: IO): Continue { return new Get(io) } /** * Purely folds over the failure and success types of this continuation. */ export function fold( failure: (e: E) => B, success: (a: A) => B ): (cont: Continue) => Continue { return (cont) => { switch (cont._tag) { case "Effect": return effect(Q.fold_(cont.query, failure, success)) case "Get": return get(T.fold_(cont.io, failure, success)) } } } /** * Effectually folds over the failure and success types of this continuation. */ export function foldCauseM( failure: (cause: C.Cause) => Q.Query, success: (a: A) => Q.Query ) { return (self: Continue): Continue => foldCauseM_(self, failure, success) } export function foldCauseM_( self: Continue, failure: (cause: C.Cause) => Q.Query, success: (a: A) => Q.Query ): Continue { switch (self._tag) { case "Effect": return effect(Q.foldCauseM_(self.query, failure, success)) case "Get": return effect(Q.foldCauseM_(Q.fromEffect(self.io), failure, success)) } } /** * Purely maps over the success type of this continuation. */ export function map( f: (a: A) => B ): (self: Continue) => Continue { return (self) => { switch (self._tag) { case "Effect": return effect(pipe(self.query, Q.map(f))) case "Get": return get(T.map_(self.io, f)) } } } export function map_( self: Continue, f: (a: A) => B ): Continue { return map(f)(self) } /** * Transforms all data sources with the specified data source aspect. */ export function mapDataSources_( self: Continue, f: DataSourceAspect ): Continue { switch (self._tag) { case "Effect": return effect(Q.mapDataSources_(self.query, f)) case "Get": return get(self.io) } } /** * Transforms all data sources with the specified data source aspect. */ export function mapDataSources( f: DataSourceAspect ): (self: Continue) => Continue { return (self) => { switch (self._tag) { case "Effect": return effect(Q.mapDataSources_(self.query, f)) case "Get": return get(self.io) } } } /** * Purely maps over the failure type of this continuation. */ export function mapError( f: (e: E) => E1 ): (self: Continue) => Continue { return (self) => { switch (self._tag) { case "Effect": return effect(Q.mapError(f)(self.query)) case "Get": return get(T.mapError_(self.io, f)) } } } /** * Effectually maps over the success type of this continuation. */ export function mapM( f: (a: A) => Q.Query ): (self: Continue) => Continue { return (self) => { switch (self._tag) { case "Effect": return effect(Q.chain_(self.query, f)) case "Get": return effect(Q.chain_(Q.fromEffect(self.io), f)) } } } /** * Purely contramaps over the environment type of this continuation. */ export function provideSome( description: string, f: (a: R0) => R ): (self: Continue) => Continue { return (self) => { switch (self._tag) { case "Effect": return effect(Q.provideSome(description, f)(self.query)) case "Get": return get(self.io) } } } /** * Combines this continuation with that continuation using the specified * function, in sequence. */ export function zipWith( that: Continue, f: (a: A, b: B) => C ) { return (self: Continue): Continue => { switch (self._tag) { case "Effect": switch (that._tag) { case "Effect": return effect(Q.zipWith(that.query, f)(self.query)) case "Get": return effect(Q.zipWith(Q.fromEffect(that.io), f)(self.query)) } case "Get": switch (that._tag) { case "Effect": return effect(Q.zipWith(that.query, f)(Q.fromEffect(self.io))) case "Get": return get(T.zipWith_(self.io, that.io, f)) } } } } /** * Combines this continuation with that continuation using the specified * function, in parallel. */ export function zipWithPar( that: Continue, f: (a: A, b: B) => C ) { return (self: Continue): Continue => { switch (self._tag) { case "Effect": switch (that._tag) { case "Effect": return effect(Q.zipWithPar(that.query, f)(self.query)) case "Get": return effect(Q.zipWith(Q.fromEffect(that.io), f)(self.query)) } case "Get": switch (that._tag) { case "Effect": return effect(Q.zipWith(that.query, f)(Q.fromEffect(self.io))) case "Get": return get(T.zipWith_(self.io, that.io, f)) } } } } /** * Runs this continuation.. */ export function runCache( cache: Cache ): (self: Continue) => T.Effect { return (self) => { switch (self._tag) { case "Effect": return Q.runCache(cache)(self.query) case "Get": return self.io } } }