1import * as Effect from "../../Effect.ts"
2import type { EntityAddress } from "../EntityAddress.ts"
3import type { ShardId } from "../ShardId.ts"
4
5const counts = new Map<string, number>()
6
7const acquire = (key: string) => {
8 counts.set(key, (counts.get(key) ?? 0) + 1)
9}
10
11const release = (key: string) => {
12 const n = counts.get(key)
13 if (n === undefined) return
14 if (n <= 1) counts.delete(key)
15 else counts.set(key, n - 1)
16}
17
18const around = <A, E, R>(key: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
19 Effect.suspend(() => {
20 acquire(key)
21 return Effect.ensuring(effect, Effect.sync(() => release(key)))
22 })
23
24const entityKey = (address: EntityAddress): string =>
25 `entity:${address.entityType}:${address.entityId}:${address.shardId.toString()}`
26
27const shardKey = (shardId: ShardId): string => `shard:${shardId.toString()}`
28
29const entityTypeKey = (entityType: string): string => `type:${entityType}`
30
31/**
32 * @internal
33 */
34export const acquireEntity = (address: EntityAddress): void => acquire(entityKey(address))
35
36/**
37 * @internal
38 */
39export const releaseEntity = (address: EntityAddress): void => release(entityKey(address))
40
41/**
42 * @internal
43 */
44export const acquireEntityType = (entityType: string): void => acquire(entityTypeKey(entityType))
45
46/**
47 * @internal
48 */
49export const releaseEntityType = (entityType: string): void => release(entityTypeKey(entityType))
50
51/**
52 * @internal
53 */
54export const aroundEntity = <A, E, R>(
55 address: EntityAddress,
56 effect: Effect.Effect<A, E, R>
57): Effect.Effect<A, E, R> => around(entityKey(address), effect)
58
59/**
60 * @internal
61 */
62export const aroundShard = <A, E, R>(
63 shardId: ShardId,
64 effect: Effect.Effect<A, E, R>
65): Effect.Effect<A, E, R> => around(shardKey(shardId), effect)
66
67/**
68 * @internal
69 */
70export const aroundEntityType = <A, E, R>(
71 entityType: string,
72 effect: Effect.Effect<A, E, R>
73): Effect.Effect<A, E, R> => around(entityTypeKey(entityType), effect)
74
75/**
76 * @internal
77 */
78export const isActive = (address: EntityAddress): boolean =>
79 counts.has(entityKey(address)) ||
80 counts.has(shardKey(address.shardId)) ||
81 counts.has(entityTypeKey(address.entityType))
82
83/**
84 * @internal
85 */
86export const isShardActive = (shardId: ShardId): boolean => counts.has(shardKey(shardId))