1import * as Context from "effect/Context";
2import * as Effect from "effect/Effect";
3import * as Option from "effect/Option";
4
5export interface NamespaceNode {
6 Id: string;
7 Parent?: NamespaceNode;
8}
9
10export class Namespace extends Context.Service<Namespace, NamespaceNode>()(
11 "Alchemy/Namespace",
12) {}
13
14export function push<const Id extends string, A, Err = never, Req = never>(
15 id: Id,
16 eff: Effect.Effect<A, Err, Req>,
17): Effect.Effect<A, Err, Req>;
18
19export function push<const Id extends string>(
20 id: Id,
21): <A, Err = never, Req = never>(
22 eff: Effect.Effect<A, Err, Req>,
23) => Effect.Effect<A, Err, Req>;
24
25export function push(id: string, eff?: Effect.Effect<any, any, any>) {
26 return eff
27 ? Effect.flatMap(CurrentNamespace, (parent) =>
28 Effect.provideService(eff, Namespace, {
29 Id: id,
30 Parent: parent,
31 }),
32 )
33 : (eff: Effect.Effect<any, any, any>) => push(id, eff);
34}
35
36export const set = (namespace: string | NamespaceNode) =>
37 Effect.provideService(
38 Namespace,
39 typeof namespace === "string" ? { Id: namespace } : namespace,
40 );
41
42export const CurrentNamespace = Effect.serviceOption(Namespace)
43
44 .pipe(Effect.map(Option.getOrUndefined));
45
46export const CurrentChain = CurrentNamespace.pipe(
47 Effect.map(function findRoot(ns): string[] {
48 if (ns?.Parent) {
49 return [ns.Id, ...findRoot(ns.Parent)];
50 }
51 return ns ? [ns.Id] : [];
52 }),
53);
54
55export const Parent = Namespace.pipe(Effect.map((ns) => ns?.Parent));
56
57export const Root = Namespace.pipe(
58 Effect.map(function findRoot(ns) {
59 if (ns.Parent) {
60 return findRoot(ns.Parent);
61 }
62 return ns;
63 }),
64);