| 8 | import * as Scope from "effect/Scope" |
| 9 | |
| 10 | export class ResourceMap<K, A, E> { |
| 11 | constructor( |
| 12 | readonly lookup: (key: K, scope: Scope.Scope) => Effect.Effect<A, E>, |
| 13 | readonly entries: MutableHashMap.MutableHashMap<K, { |
| 14 | readonly scope: Scope.CloseableScope |
| 15 | readonly deferred: Deferred.Deferred<A, E> |
| 16 | }>, |
| 17 | readonly isClosed: MutableRef.MutableRef<boolean> |
| 18 | ) {} |
| 19 | |
| 20 | static make = Effect.fnUntraced(function*<K, A, E, R>(lookup: (key: K) => Effect.Effect<A, E, R>) { |
| 21 | const scope = yield* Effect.scope |
| 22 | const context = yield* Effect.context<R>() |
| 23 | const isClosed = MutableRef.make(false) |
| 24 | |
| 25 | const entries = MutableHashMap.empty<K, { |
| 26 | scope: Scope.CloseableScope |
| 27 | deferred: Deferred.Deferred<A, E> |
| 28 | }>() |
| 29 | |
| 30 | yield* Scope.addFinalizerExit( |
| 31 | scope, |
| 32 | (exit) => { |
| 33 | MutableRef.set(isClosed, true) |
| 34 | return Effect.forEach(entries, ([key, { scope }]) => { |
| 35 | MutableHashMap.remove(entries, key) |
| 36 | return Effect.exit(Scope.close(scope, exit)) |
| 37 | }, { concurrency: "unbounded", discard: true }) |
| 38 | } |
| 39 | ) |
| 40 | |
| 41 | return new ResourceMap( |
| 42 | (key, scope) => Effect.provide(lookup(key), Context.add(context, Scope.Scope, scope)), |
| 43 | entries, |
| 44 | isClosed |
| 45 | ) |
| 46 | }) |
| 47 | |
| 48 | get(key: K): Effect.Effect<A, E> { |
| 49 | return Effect.withFiberRuntime((fiber) => { |
| 50 | if (MutableRef.get(this.isClosed)) { |
| 51 | return Effect.interrupt |
| 52 | } |
| 53 | const existing = MutableHashMap.get(this.entries, key) |
| 54 | if (Option.isSome(existing)) { |
| 55 | return Deferred.await(existing.value.deferred) |
| 56 | } |
| 57 | const scope = Effect.runSync(Scope.make()) |
| 58 | const deferred = Deferred.unsafeMake<A, E>(fiber.id()) |
| 59 | MutableHashMap.set(this.entries, key, { scope, deferred }) |
| 60 | return Effect.onExit(this.lookup(key, scope), (exit) => { |
| 61 | if (exit._tag === "Success") { |
| 62 | return Deferred.done(deferred, exit) |
| 63 | } |
| 64 | MutableHashMap.remove(this.entries, key) |
| 65 | return Deferred.done(deferred, exit) |
| 66 | }) |
| 67 | }) |
nothing calls this directly
no test coverage detected
searching dependent graphs…