import type { Tag } from "@tsplus/stdlib/service/Tag" /** * @tsplus type Env.Ops */ export interface EnvOps { readonly sym: unique symbol readonly empty: Env (tag: Tag, service: H): Env new(unsafeMap: Env["unsafeMap"]): Env } function methodAdd(this: Env, tag: Tag, service: H): Env { const map = new Map(this.unsafeMap) // @ts-expect-error map.set(tag, service) return new Env(map) as Env } function methodGet(this: Env, tag: Tag): S { // @ts-expect-error if (!this.unsafeMap.has(tag)) { throw new NoSuchElement() } // @ts-expect-error return this.unsafeMap.get(tag)! as S } function methodGetMaybe(this: Env, tag: Tag): Maybe { // @ts-expect-error return this.unsafeMap.has(tag) ? Maybe.some(this.unsafeMap.get(tag)! as S) : Maybe.none } function methodMerge(this: Env, that: Env): Env { const map = new Map(this.unsafeMap) for (const [tag, s] of that.unsafeMap) { map.set(tag, s) } return new Env(map) as Env } function pruneMethod[]>( this: Env, ...tags: S ): Env<{ [k in keyof S]: Tag.TagType }[number]> { const tagSet = new Set(tags) const newEnv = new Map() for (const [tag, s] of this.unsafeMap.entries()) { // @ts-expect-error if (tagSet.has(tag)) { newEnv.set(tag, s) } } return new Env(newEnv) } const sym = Symbol("@tsplus/stdlib/Env/Env") as EnvOps["sym"] export const Env: EnvOps = Object.assign( function self(this: any, a: any, b: any) { if (this != null && this.constructor === self) { return createEnv(a) } return Env.empty.add(a, b) } as any as EnvOps, { sym, empty: createEnv(new Map()) } ) function createEnv(unsafeMap: Env["unsafeMap"]) { return { [sym]: identity, add: methodAdd, get: methodGet, unsafeGet: methodGet, getMaybe: methodGetMaybe, merge: methodMerge, prune: pruneMethod, unsafeMap } } export type Tags = R extends infer S ? Tag : never /** * @tsplus type Env */ export interface Env { readonly [Env.sym]: (_: never) => R readonly unsafeMap: Map, unknown> add(this: Env, tag: Tag, service: H): Env get>(this: Env, tag: T): T extends Tag ? S : never unsafeGet(this: Env, tag: Tag): S getMaybe(this: Env, tag: Tag): Maybe merge(this: Env, that: Env): Env prune[]>( this: Env, ...tags: S ): Env<{ [k in keyof S]: Tag.TagType }[number]> }