Skip to content

Commit

Permalink
Add Context.mergeAll (#4135)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuentinJanuel authored and effect-bot committed Dec 18, 2024
1 parent 6f3fa14 commit f5e1d60
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/rare-eagles-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Add Context.mergeAll to combine multiple Contexts into one.
30 changes: 30 additions & 0 deletions packages/effect/src/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,36 @@ export const merge: {
<Services, R1>(self: Context<Services>, that: Context<R1>): Context<Services | R1>
} = internal.merge

/**
* Merges any number of `Context`s, returning a new `Context` containing the services of all.
*
* @param ctxs - The `Context`s to merge.
*
* @example
* ```ts
* import { Context } from "effect"
*
* const Port = Context.GenericTag<{ PORT: number }>("Port")
* const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
* const Host = Context.GenericTag<{ HOST: string }>("Host")
*
* const firstContext = Context.make(Port, { PORT: 8080 })
* const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
* const thirdContext = Context.make(Host, { HOST: "localhost" })
*
* const Services = Context.mergeAll(firstContext, secondContext, thirdContext)
*
* assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
* assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })
* assert.deepStrictEqual(Context.get(Services, Host), { HOST: "localhost" })
* ```
*
* @since 3.12.0
*/
export const mergeAll: <T extends Array<unknown>>(
...ctxs: [...{ [K in keyof T]: Context<T[K]> }]
) => Context<T[number]> = internal.mergeAll

/**
* Returns a new `Context` that contains only the specified services.
*
Expand Down
13 changes: 13 additions & 0 deletions packages/effect/src/internal/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,19 @@ export const merge = dual<
return makeContext(map)
})

/** @internal */
export const mergeAll = <T extends Array<unknown>>(
...ctxs: [...{ [K in keyof T]: C.Context<T[K]> }]
): C.Context<T[number]> => {
const map = new Map()
for (const ctx of ctxs) {
for (const [tag, s] of ctx.unsafeMap) {
map.set(tag, s)
}
}
return makeContext(map)
}

/** @internal */
export const pick =
<Services, S extends Array<C.ValidTagsById<Services>>>(...tags: S) =>
Expand Down
33 changes: 33 additions & 0 deletions packages/effect/test/Context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,39 @@ describe("Context", () => {
expect(result.pipe(Context.get(A))).toEqual({ a: 0 })
})

it("mergeAll", () => {
const env = Context.mergeAll(
Context.make(A, { a: 0 }),
Context.make(B, { b: 1 }),
Context.make(C, { c: 2 })
)

const pruned = pipe(
env,
Context.pick(A, B)
)

expect(pipe(
pruned,
Context.get(A)
)).toEqual({ a: 0 })

expect(pipe(
pruned,
Context.getOption(B)
)).toEqual(O.some({ b: 1 }))

expect(pipe(
pruned,
Context.getOption(C)
)).toEqual(O.none())

expect(pipe(
env,
Context.getOption(C)
)).toEqual(O.some({ c: 2 }))
})

it("isContext", () => {
expect(Context.isContext(Context.empty())).toEqual(true)
expect(Context.isContext(null)).toEqual(false)
Expand Down

0 comments on commit f5e1d60

Please sign in to comment.