Skip to content

Commit

Permalink
feat: add repository to collect multiple Fns. Remove FnClass
Browse files Browse the repository at this point in the history
  • Loading branch information
TylorS committed Jul 26, 2023
1 parent 79593d0 commit fb5cc8a
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 95 deletions.
25 changes: 0 additions & 25 deletions packages/context/src/fn-class.test.ts

This file was deleted.

69 changes: 0 additions & 69 deletions packages/context/src/fn-class.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/context/src/fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export namespace Fn {

export type FnOf<T extends Fn<any, any>> = T extends Fn<any, infer F> ? F : never

export type Any = Fn<any, any>

export const wrap = <I, S extends EffectFn>(tag: Tag<I, S>): Fn<I, S> => {
const implement = <T2 extends EffectFn.Extendable<S>>(
implementation: T2,
Expand Down
2 changes: 1 addition & 1 deletion packages/context/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export * from './builder.js'
export * from './context.js'
export * from './fn-class.js'
export * from './fn.js'
export * from './identifier.js'
export * from './interfaces.js'
export * from './many.js'
export * from './ref.js'
export * from './repository.js'
export * from './subject.js'
62 changes: 62 additions & 0 deletions packages/context/src/repository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as Effect from '@effect/io/Effect'
import { describe, expect, it } from 'vitest'

import { Fn } from './fn.js'
import { id } from './identifier.js'
import { repository } from './repository.js'

class FooService extends id('FooService') {}
const Foo = Fn<(foo: string) => Effect.Effect<never, never, string>>()(FooService)

class BarService extends id('BarService') {}
const Bar = Fn<(bar: string) => Effect.Effect<never, never, string>>()(BarService)

const FooBar = repository({ foo: Foo, bar: Bar })

describe(repository.name, () => {
it('allows combining multiple functions into a single layer', async () => {
const test: Effect.Effect<FooService | BarService, never, string> = Effect.gen(function* (_) {
const foo = yield* _(FooBar.foo('foo'))
const bar = yield* _(FooBar.bar('bar'))

return foo + bar
})

expect(
await Effect.runPromise(
test.pipe(
Effect.provideLayer(
FooBar.implement({
foo: (x) => Effect.sync(() => 'foo' + x),
bar: (x) => Effect.sync(() => 'bar' + x),
}),
),
),
),
).toBe('foofoobarbar')
})

it('allows retrieving the full service', async () => {
const test: Effect.Effect<FooService | BarService, never, string> = Effect.gen(function* (_) {
const service = yield* _(FooBar)

const foo = yield* _(service.foo('foo'))
const bar = yield* _(service.bar('bar'))

return foo + bar
})

expect(
await Effect.runPromise(
test.pipe(
Effect.provideLayer(
FooBar.implement({
foo: (x) => Effect.sync(() => 'foo' + x),
bar: (x) => Effect.sync(() => 'bar' + x),
}),
),
),
),
).toBe('foofoobarbar')
})
})
43 changes: 43 additions & 0 deletions packages/context/src/repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as Layer from '@effect/io/Layer'

import { EffectFn, Fn } from './fn.js'
import { TaggedStruct, struct } from './many.js'

type AnyFns = Readonly<Record<string, Fn.Any>>

export function repository<Fns extends AnyFns>(input: Fns): Repository<Fns> {
const entries = Object.entries(input)

const fns = Object.fromEntries(entries.map(([k, v]) => [k, v.apply])) as RepositoryFns<Fns>

const implement: RepositoryImplement<Fns>['implement'] = (implementations) => {
const [first, ...rest] = entries.map(([key, fn]) => fn.implement(implementations[key]))

return Layer.mergeAll(first, ...rest)
}

return {
...fns,
...struct(input),
implement,
functions: input,
}
}

export type Repository<Fns extends AnyFns> = RepositoryFns<Fns> &
TaggedStruct<Fns> &
RepositoryImplement<Fns> & {
readonly functions: Fns
}

export type RepositoryFns<Fns extends AnyFns> = {
readonly [K in keyof Fns]: Fns[K]['apply']
}

export type RepositoryImplement<Fns extends AnyFns> = {
readonly implement: <
Impls extends { readonly [K in keyof Fns]: EffectFn.Extendable<Fn.FnOf<Fns[K]>> },
>(
implementations: Impls,
) => Layer.Layer<EffectFn.ResourcesOf<Impls[keyof Impls]>, never, Fn.KeyOf<Fns[keyof Fns]>>
}

0 comments on commit fb5cc8a

Please sign in to comment.