type State = PullBoth | PullLeft | PullRight class PullBoth { readonly _tag = "PullBoth" } class PullLeft { readonly _tag = "PullLeft" constructor(readonly rightChunk: Chunk) {} } class PullRight { readonly _tag = "PullRight" constructor(readonly leftChunk: Chunk) {} } /** * Zips this stream with another point-wise and applies the function to the * paired elements. * * The new stream will end when one of the sides ends. * * @tsplus static effect/core/stream/Stream.Aspects zipWithChunks * @tsplus pipeable effect/core/stream/Stream zipWithChunks */ export function zipWithChunks( that: Stream, f: ( leftChunk: Chunk, rightChunk: Chunk ) => readonly [Chunk, Either, Chunk>] ) { return (self: Stream): Stream => self.combineChunks(that, new PullBoth() as State, pull(f)) } function zipWithChunksInternal( leftChunk: Chunk, rightChunk: Chunk, f: ( leftChunk: Chunk, rightChunk: Chunk ) => readonly [Chunk, Either, Chunk>] ): readonly [Chunk, State] { const [out, either] = f(leftChunk, rightChunk) return either.fold( (leftChunk) => leftChunk.isEmpty ? [out, new PullBoth()] : [out, new PullRight(leftChunk)], (rightChunk) => rightChunk.isEmpty ? [out, new PullBoth()] : [out, new PullLeft(rightChunk)] ) } function pull( f: ( leftChunk: Chunk, rightChunk: Chunk ) => readonly [Chunk, Either, Chunk>] ) { return ( state: State, pullLeft: Effect, Chunk>, pullRight: Effect, Chunk> ): Effect, readonly [Chunk, State]>> => { switch (state._tag) { case "PullBoth": { return pullLeft.unsome .zipPar(pullRight.unsome) .foldEffect( (err) => Effect.succeed(Exit.fail(Maybe.some(err))), ([left, right]) => { if (left.isSome() && right.isSome()) { const leftChunk = left.value const rightChunk = right.value if (leftChunk.isEmpty && rightChunk.isEmpty) { return pull(f)(new PullBoth(), pullLeft, pullRight) } else if (leftChunk.isEmpty) { return pull(f)(new PullLeft(rightChunk), pullLeft, pullRight) } else if (rightChunk.isEmpty) { return pull(f)(new PullRight(leftChunk), pullLeft, pullRight) } else { return Effect.succeed( Exit.succeed(zipWithChunksInternal(leftChunk, rightChunk, f)) ) } } return Effect.succeed(Exit.fail(Maybe.none)) } ) } case "PullLeft": { return pullLeft.foldEffect( (err) => Effect.succeed(Exit.fail(err)), (leftChunk) => leftChunk.isEmpty ? pull(f)(new PullLeft(state.rightChunk), pullLeft, pullRight) : state.rightChunk.isEmpty ? pull(f)(new PullRight(leftChunk), pullLeft, pullRight) : Effect.succeed( Exit.succeed(zipWithChunksInternal(leftChunk, state.rightChunk, f)) ) ) } case "PullRight": { return pullRight.foldEffect( (err) => Effect.succeed(Exit.fail(err)), (rightChunk) => rightChunk.isEmpty ? pull(f)(new PullRight(state.leftChunk), pullLeft, pullRight) : state.leftChunk.isEmpty ? pull(f)(new PullLeft(rightChunk), pullLeft, pullRight) : Effect.succeed( Exit.succeed(zipWithChunksInternal(state.leftChunk, rightChunk, f)) ) ) } } } }