diff --git a/packages/effect-lens/src/PropertyPath.test.ts b/packages/effect-lens/src/PropertyPath.test.ts deleted file mode 100644 index 4c68a38..0000000 --- a/packages/effect-lens/src/PropertyPath.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { describe, expect, test } from "bun:test" -import { Option } from "effect" -import * as PropertyPath from "./PropertyPath.js" - - -describe("immutableSet with arrays", () => { - test("sets a top-level array element", () => { - const arr = [1, 2, 3] - const result = PropertyPath.immutableSet(arr, [1], 99) - expect(result).toEqual(Option.some([1, 99, 3])) - }) - - test("does not mutate the original array", () => { - const arr = [1, 2, 3] - PropertyPath.immutableSet(arr, [0], 42) - expect(arr).toEqual([1, 2, 3]) - }) - - test("sets the first element of an array", () => { - const arr = ["a", "b", "c"] - const result = PropertyPath.immutableSet(arr, [0], "z") - expect(result).toEqual(Option.some(["z", "b", "c"])) - }) - - test("sets the last element of an array", () => { - const arr = [10, 20, 30] - const result = PropertyPath.immutableSet(arr, [2], 99) - expect(result).toEqual(Option.some([10, 20, 99])) - }) - - test("sets a nested array element inside an object", () => { - const obj = { tags: ["foo", "bar", "baz"] } - const result = PropertyPath.immutableSet(obj, ["tags", 1], "qux") - expect(result).toEqual(Option.some({ tags: ["foo", "qux", "baz"] })) - }) - - test("sets a deeply nested value inside an array of objects", () => { - const obj = { items: [{ name: "alice" }, { name: "bob" }] } - const result = PropertyPath.immutableSet(obj, ["items", 0, "name"], "charlie") - expect(result).toEqual(Option.some({ items: [{ name: "charlie" }, { name: "bob" }] })) - }) - - test("sets a value in a nested array", () => { - const matrix = [[1, 2], [3, 4]] - const result = PropertyPath.immutableSet(matrix, [1, 0], 99) - expect(result).toEqual(Option.some([[1, 2], [99, 4]])) - }) - - test("returns Option.none() for an out-of-bounds index", () => { - const arr = [1, 2, 3] - const result = PropertyPath.immutableSet(arr, [5], 99) - expect(result).toEqual(Option.none()) - }) - - test("returns Option.none() for a non-numeric key on an array", () => { - const arr = [1, 2, 3] - // @ts-expect-error intentionally wrong key type - const result = PropertyPath.immutableSet(arr, ["length"], 0) - expect(result).toEqual(Option.none()) - }) - - test("empty path returns Option.some of the value itself", () => { - const arr = [1, 2, 3] - const result = PropertyPath.immutableSet(arr, [], [9, 9, 9] as any) - expect(result).toEqual(Option.some([9, 9, 9])) - }) -}) diff --git a/packages/effect-lens/src/PropertyPath.ts b/packages/effect-lens/src/PropertyPath.ts deleted file mode 100644 index b73d24d..0000000 --- a/packages/effect-lens/src/PropertyPath.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Array, Equivalence, Function, Option, Predicate } from "effect" - - -export type PropertyPath = readonly PropertyKey[] - -type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - -export type Paths = readonly [] | ( - D extends never ? readonly [] : - T extends Seen ? readonly [] : - T extends readonly any[] ? { - [K in keyof T as K extends number ? K : never]: - | readonly [K] - | readonly [K, ...Paths] - } extends infer O - ? O[keyof O] - : never - : - T extends object ? { - [K in keyof T as K extends string | number | symbol ? K : never]-?: - NonNullable extends infer V - ? readonly [K] | readonly [K, ...Paths] - : never - } extends infer O - ? O[keyof O] - : never - : - never -) - -export type ValueFromPath = P extends readonly [infer Head, ...infer Tail] - ? Head extends keyof T - ? ValueFromPath - : T extends readonly any[] - ? Head extends number - ? ValueFromPath - : never - : never - : T - - -export const equivalence: Equivalence.Equivalence = Equivalence.array(Equivalence.strict()) - -export const unsafeGet: { - >(path: P): (self: T) => ValueFromPath - >(self: T, path: P): ValueFromPath -} = Function.dual(2, >(self: T, path: P): ValueFromPath => - path.reduce((acc: any, key: any) => acc?.[key], self) -) - -export const get: { - >(path: P): (self: T) => Option.Option> - >(self: T, path: P): Option.Option> -} = Function.dual(2, >(self: T, path: P): Option.Option> => - path.reduce( - (acc: Option.Option, key: any): Option.Option => Option.isSome(acc) - ? Predicate.hasProperty(acc.value, key) - ? Option.some(acc.value[key]) - : Option.none() - : acc, - - Option.some(self), - ) -) - -export const immutableSet: { - >(path: P, value: ValueFromPath): (self: T) => Option.Option - >(self: T, path: P, value: ValueFromPath): Option.Option -} = Function.dual(3, >(self: T, path: P, value: ValueFromPath): Option.Option => { - const key = Array.head(path as PropertyPath) - if (Option.isNone(key)) - return Option.some(value as T) - if (!Predicate.hasProperty(self, key.value)) - return Option.none() - - const child = immutableSet(self[key.value], Option.getOrThrow(Array.tail(path as PropertyPath)), value) - if (Option.isNone(child)) - return child - - if (Array.isArray(self)) - return typeof key.value === "number" - ? Option.some([ - ...self.slice(0, key.value), - child.value, - ...self.slice(key.value + 1), - ] as T) - : Option.none() - - if (typeof self === "object") - return Option.some( - Object.assign( - Object.create(Object.getPrototypeOf(self)), - { ...self, [key.value]: child.value }, - ) - ) - - return Option.none() -}) diff --git a/packages/effect-lens/src/index.ts b/packages/effect-lens/src/index.ts index 6eec098..24e4bb8 100644 --- a/packages/effect-lens/src/index.ts +++ b/packages/effect-lens/src/index.ts @@ -1,3 +1,2 @@ export * as Lens from "./Lens.js" -export * as PropertyPath from "./PropertyPath.js" export * as Subscribable from "./Subscribable.js"