// tracing: off
import "../../../Operator"
import * as Cause from "../../../Cause"
import * as Tp from "../../../Collections/Immutable/Tuple"
import * as T from "../../../Effect"
import * as E from "../../../Either"
import { identity } from "../../../Function"
import * as ReleaseMap from "../../../Managed/ReleaseMap"
import * as P from "./_internal/primitives"
import {
  BracketOut,
  Bridge,
  ConcatAll,
  Done,
  Emit,
  Ensuring,
  Fold,
  Halt,
  PipeTo,
  Provide,
  Read
} from "./_internal/primitives"
/**
 * Pipe the output of a channel into the input of another
 */

export function pipeTo_(left, right) {
  return new PipeTo(
    () => left,
    () => right
  )
}
/**
 * Pipe the output of a channel into the input of another
 *
 * @dataFirst pipeTo_
 */

export function pipeTo(right) {
  return (left) => pipeTo_(left, right)
}
/**
 * Reads an input and continue exposing both full error cause and completion
 */

export function readWithCause(inp, halt, done) {
  return new Read(inp, new P.ContinuationK(done, halt))
}
/**
 * Reads an input and continue exposing both error and completion
 */

export function readWith(inp, error, done) {
  return readWithCause(inp, (c) => E.fold_(Cause.failureOrCause(c), error, halt), done)
}
/**
 * End a channel with the specified result
 */

export function endWith(result) {
  return new Done(result)
}
/**
 * End a channel with the specified result
 */

export function end(result) {
  return new Done(() => result)
}
/**
 * Halt a channel with the specified cause
 */

export function haltWith(result) {
  return new Halt(result)
}
/**
 * Halt a channel with the specified cause
 */

export function halt(result) {
  return new Halt(() => result)
}
/**
 * Halt a channel with the specified error
 */

export function failWith(error) {
  return new Halt(() => Cause.fail(error()))
}
/**
 * Halt a channel with the specified error
 */

export function fail(error) {
  return new Halt(() => Cause.fail(error))
}
/**
 * Halt a channel with the specified exception
 */

export function die(defect) {
  return new Halt(() => Cause.die(defect))
}
/**
 * Halt a channel with the specified exception
 */

export function dieWith(defect) {
  return new Halt(() => Cause.die(defect()))
}
/**
 * Writes an output to the channel
 */

export function writeWith(out) {
  return new Emit(out)
}
/**
 * Writes an output to the channel
 */

export function write(out) {
  return new Emit(() => out)
}
/**
 * Returns a new channel with an attached finalizer. The finalizer is guaranteed to be executed
 * so long as the channel begins execution (and regardless of whether or not it completes).
 */

export function ensuringWith_(channel, finalizer) {
  return new Ensuring(channel, finalizer)
}
/**
 * Returns a new channel with an attached finalizer. The finalizer is guaranteed to be executed
 * so long as the channel begins execution (and regardless of whether or not it completes).
 *
 * @dataFirst ensuringWith_
 */

export function ensuringWith(finalizer) {
  return (channel) => ensuringWith_(channel, finalizer)
}
/**
 * Returns a new channel whose outputs are fed to the specified factory function, which creates
 * new channels in response. These new channels are sequentially concatenated together, and all
 * their outputs appear as outputs of the newly returned channel. The provided merging function
 * is used to merge the terminal values of all channels into the single terminal value of the
 * returned channel.
 */

export function concatMapWith_(self, f, g, h) {
  return new ConcatAll(g, h, self, f)
}
/**
 * Returns a new channel whose outputs are fed to the specified factory function, which creates
 * new channels in response. These new channels are sequentially concatenated together, and all
 * their outputs appear as outputs of the newly returned channel. The provided merging function
 * is used to merge the terminal values of all channels into the single terminal value of the
 * returned channel.
 *
 * @dataFirst concatMapWith_
 */

export function concatMapWith(f, g, h) {
  return (self) => concatMapWith_(self, f, g, h)
}
/**
 * Concat sequentially a channel of channels
 */

export function concatAllWith_(channels, f, g) {
  return new ConcatAll(f, g, channels, identity)
}
/**
 * Concat sequentially a channel of channels
 *
 * @dataFirst concatAllWith_
 */

export function concatAllWith(f, g) {
  return (channels) => concatAllWith_(channels, f, g)
}
/**
 * Returns a new channel whose outputs are fed to the specified factory function, which creates
 * new channels in response. These new channels are sequentially concatenated together, and all
 * their outputs appear as outputs of the newly returned channel.
 */

export function concatMap_(self, f) {
  return concatMapWith_(
    self,
    f,
    () => void 0,
    () => void 0
  )
}
/**
 * Returns a new channel whose outputs are fed to the specified factory function, which creates
 * new channels in response. These new channels are sequentially concatenated together, and all
 * their outputs appear as outputs of the newly returned channel.
 *
 * @dataFirst concatMap_
 */

export function concatMap(f) {
  return (self) => concatMap_(self, f)
}
/**
 * Fold the channel exposing success and full error cause
 */

export function foldCauseM_(self, onErr, onSucc) {
  return new Fold(self, new P.ContinuationK(onSucc, onErr))
}
/**
 * Fold the channel exposing success and full error cause
 *
 * @dataFirst foldCauseM_
 */

export function foldCauseM(onErr, onSucc) {
  return (self) => foldCauseM_(self, onErr, onSucc)
}
/**
 * Embed inputs from continuos pulling of a producer
 */

export function embedInput_(self, input) {
  return new Bridge(input, self)
}
/**
 * Embed inputs from continuos pulling of a producer
 *
 * @dataFirst embedInput_
 */

export function embedInput(input) {
  return (self) => embedInput_(self, input)
}
/**
 * Construct a resource Channel with Acquire / Release
 */

export function bracketOutExit_(self, release) {
  return new BracketOut(self, release)
}
/**
 * Construct a resource Channel with Acquire / Release
 *
 * @dataFirst bracketOutExit_
 */

export function bracketOutExit(release) {
  return (self) => bracketOutExit_(self, release)
}
/**
 * Provides the channel with its required environment, which eliminates
 * its dependency on `Env`.
 */

export function provideAll_(self, env) {
  return new Provide(env, self)
}
/**
 * Provides the channel with its required environment, which eliminates
 * its dependency on `Env`.
 *
 * @dataFirst provideAll_
 */

export function provideAll(env) {
  return (self) => provideAll_(self, env)
}
/**
 * Returns a new channel, which sequentially combines this channel, together with the provided
 * factory function, which creates a second channel based on the terminal value of this channel.
 * The result is a channel that will first perform the functions of this channel, before
 * performing the functions of the created channel (including yielding its terminal value).
 */

export function chain_(self, f) {
  return new P.Fold(self, new P.ContinuationK(f, halt))
}
/**
 * Returns a new channel, which sequentially combines this channel, together with the provided
 * factory function, which creates a second channel based on the terminal value of this channel.
 * The result is a channel that will first perform the functions of this channel, before
 * performing the functions of the created channel (including yielding its terminal value).
 *
 * @dataFirst chain_
 */

export function chain(f) {
  return (self) => chain_(self, f)
}
/**
 * Returns a new channel which reads all the elements from upstream's output channel
 * and ignores them, then terminates with the upstream result value.
 */

export function drain(self) {
  const drainer = readWithCause((_) => drainer, halt, end)
  return self[">>>"](drainer)
}
/**
 * Use an effect to end a channel
 */

export function fromEffect(self) {
  return new P.Effect(self)
}
/**
 * Use a managed to emit an output element
 */

export function managedOut(self) {
  return concatMap_(
    bracketOutExit_(ReleaseMap.makeReleaseMap, (rm, ex) =>
      ReleaseMap.releaseAll(ex, T.sequential)(rm)
    ),
    (rm) =>
      chain_(
        fromEffect(
          T.map_(
            T.provideSome_(self.effect, (r) => Tp.tuple(r, rm)),
            Tp.get(1)
          )
        ),
        write
      )
  )
}
/**
 * Returns a new channel, which flattens the terminal value of this channel. This function may
 * only be called if the terminal value of this channel is another channel of compatible types.
 */

export function flatten(self) {
  return chain_(self, identity)
}
/**
 * Makes a channel from an effect that returns a channel in case of success
 */

export function unwrap(self) {
  return flatten(fromEffect(self))
}
/**
 * Makes a channel from a managed that returns a channel in case of success
 */

export function unwrapManaged(self) {
  return concatAllWith_(managedOut(self), identity, identity)
}
/**
 * Unit channel
 */

export const unit = /*#__PURE__*/ end(void 0)
export function succeedWith(effect) {
  return new P.EffectTotal(effect)
}
export function suspend(effect) {
  return new P.EffectSuspendTotal(effect)
}
//# sourceMappingURL=core.js.map
