Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@
"types": "./dist/logger/shared.d.ts",
"default": "./dist/logger/shared.js"
},
"./pipegen": {
"types": "./dist/pipegen.d.ts",
"default": "./dist/pipegen.js"
},
"./rateLimit": {
"types": "./dist/rateLimit.d.ts",
"default": "./dist/rateLimit.js"
Expand Down
96 changes: 96 additions & 0 deletions packages/infra/src/pipegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* eslint-disable unused-imports/no-unused-vars */
/* eslint-disable prefer-rest-params */
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
import { Effect } from "effect-app"
import { type YieldWrap } from "effect/Utils"

// inspired by Effect.fnUntraced
export type FromGenToEffect<AEff, Eff> =
& Effect.Effect<
AEff,
[Eff] extends [never] ? never
: [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
: never,
[Eff] extends [never] ? never
: [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
: never
>
& {} // so it computes

// inspired by Effect.fnUntraced
export type EffectifyIfGen<A> = A extends
Generator<infer Eff extends YieldWrap<Effect.Effect<any, any, any>>, infer AEff, never> ? FromGenToEffect<AEff, Eff>
: A extends () => Generator<infer Eff extends YieldWrap<Effect.Effect<any, any, any>>, infer AEff, never>
? FromGenToEffect<AEff, Eff>
: A

export function pipeGen<A>(
a: A
): EffectifyIfGen<A>
export function pipeGen<A, B>(
a: A,
ab: (_: EffectifyIfGen<A>) => B
): EffectifyIfGen<B>
export function pipeGen<A, B, C>(
a: A,
ab: (_: EffectifyIfGen<A>) => B,
bc: (_: EffectifyIfGen<B>) => C
): EffectifyIfGen<C>
export function pipeGen<A, B, C, D>(
a: A,
ab: (value: EffectifyIfGen<A>) => B,
bc: (value: EffectifyIfGen<B>) => C,
cd: (value: EffectifyIfGen<C>) => D
): EffectifyIfGen<D>
export function pipeGen<A, B, C, D, E>(
a: A,
ab: (value: EffectifyIfGen<A>) => B,
bc: (value: EffectifyIfGen<B>) => C,
cd: (value: EffectifyIfGen<C>) => D,
de: (value: EffectifyIfGen<D>) => E
): EffectifyIfGen<E>
export function pipeGen<A, B, C, D, E, F>(
a: A,
ab: (value: EffectifyIfGen<A>) => B,
bc: (value: EffectifyIfGen<B>) => C,
cd: (value: EffectifyIfGen<C>) => D,
de: (value: EffectifyIfGen<D>) => E,
ef: (value: EffectifyIfGen<E>) => F
): EffectifyIfGen<F>
export function pipeGen<A, B, C, D, E, F, G>(
a: A,
ab: (value: EffectifyIfGen<A>) => B,
bc: (value: EffectifyIfGen<B>) => C,
cd: (value: EffectifyIfGen<C>) => D,
de: (value: EffectifyIfGen<D>) => E,
ef: (value: EffectifyIfGen<E>) => F,
fg: (value: EffectifyIfGen<F>) => G
): EffectifyIfGen<G>
export function pipeGen<A, B, C, D, E, F, G, H>(
a: A,
ab: (value: EffectifyIfGen<A>) => B,
bc: (value: EffectifyIfGen<B>) => C,
cd: (value: EffectifyIfGen<C>) => D,
de: (value: EffectifyIfGen<D>) => E,
ef: (value: EffectifyIfGen<E>) => F,
fg: (value: EffectifyIfGen<F>) => G,
gh: (value: EffectifyIfGen<G>) => H
): EffectifyIfGen<H>
export function pipeGen(
a: unknown,
_ab?: Function,
_bc?: Function,
_cd?: Function,
_de?: Function,
_ef?: Function,
_fg?: Function,
_gh?: Function
): unknown {
let ret = (a as any)[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(a as any)() : a
for (let i = 1; i < arguments.length; i++) {
ret = (arguments[i][Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(arguments[i]) : arguments[i])(
Comment thread
jfet97 marked this conversation as resolved.
ret
)
Comment thread
jfet97 marked this conversation as resolved.
}
return ret
}
40 changes: 40 additions & 0 deletions packages/infra/test/pipegen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, expectTypeOf, it } from "@effect/vitest"
import { Effect } from "effect-app"
import { pipeGen } from "../src/pipegen.js"

it(
"works",
Effect.fnUntraced(function*() {
const res = pipeGen(
19,
(n) => n * 10,
function*(n) {
return yield* Effect.succeed(n / 2)
},
Effect.map((n) => String(n + 1))
)
expectTypeOf(res).toEqualTypeOf<Effect.Effect<string>>()
expect(yield* res).toEqual("96")

const res2 = pipeGen(
function*() {
return yield* Effect.succeed(8)
},
Effect.map((n) => String(n + 1))
)
expectTypeOf(res2).toEqualTypeOf<Effect.Effect<string>>()
expect(yield* res2).toEqual("9")

const res3 = pipeGen(
function*() {
return yield* Effect.succeed(8)
},
Effect.map((n) => String(n + 1)),
function*(e) {
return (yield* e).repeat(2).length > 3
}
)
expectTypeOf(res3).toEqualTypeOf<Effect.Effect<boolean>>()
expect(yield* res3).toEqual(false)
}, Effect.runPromise)
)