import type { Chunk } from "@effect/data/Chunk" import type { Context } from "@effect/data/Context" import type * as D from "@effect/data/Differ" import type { Either } from "@effect/data/Either" import * as Equal from "@effect/data/Equal" import * as Dual from "@effect/data/Function" import { constant, identity } from "@effect/data/Function" import type { HashMap } from "@effect/data/HashMap" import type { HashSet } from "@effect/data/HashSet" import * as ChunkPatch from "@effect/data/internal/Differ/ChunkPatch" import * as ContextPatch from "@effect/data/internal/Differ/ContextPatch" import * as HashMapPatch from "@effect/data/internal/Differ/HashMapPatch" import * as HashSetPatch from "@effect/data/internal/Differ/HashSetPatch" import * as OrPatch from "@effect/data/internal/Differ/OrPatch" /** @internal */ export const DifferTypeId: D.TypeId = Symbol.for("@effect/data/Differ") as D.TypeId /** @internal */ export const DifferProto = { [DifferTypeId]: { _P: identity, _V: identity } } /** @internal */ export const make = ( params: { readonly empty: Patch readonly diff: (oldValue: Value, newValue: Value) => Patch readonly combine: (first: Patch, second: Patch) => Patch readonly patch: (patch: Patch, oldValue: Value) => Value } ): D.Differ => { const differ = Object.create(DifferProto) differ.empty = params.empty differ.diff = params.diff differ.combine = params.combine differ.patch = params.patch return differ } /** @internal */ export const environment = (): D.Differ, D.Differ.Context.Patch> => make({ empty: ContextPatch.empty(), combine: (first, second) => ContextPatch.combine(second)(first), diff: (oldValue, newValue) => ContextPatch.diff(oldValue, newValue), patch: (patch, oldValue) => ContextPatch.patch(oldValue)(patch) }) /** @internal */ export const chunk = ( differ: D.Differ ): D.Differ, D.Differ.Chunk.Patch> => make({ empty: ChunkPatch.empty(), combine: (first, second) => ChunkPatch.combine(second)(first), diff: (oldValue, newValue) => ChunkPatch.diff({ oldValue, newValue, differ }), patch: (patch, oldValue) => ChunkPatch.patch(oldValue, differ)(patch) }) /** @internal */ export const hashMap = ( differ: D.Differ ): D.Differ, D.Differ.HashMap.Patch> => make({ empty: HashMapPatch.empty(), combine: (first, second) => HashMapPatch.combine(second)(first), diff: (oldValue, newValue) => HashMapPatch.diff({ oldValue, newValue, differ }), patch: (patch, oldValue) => HashMapPatch.patch(oldValue, differ)(patch) }) /** @internal */ export const hashSet = (): D.Differ, D.Differ.HashSet.Patch> => make({ empty: HashSetPatch.empty(), combine: (first, second) => HashSetPatch.combine(second)(first), diff: (oldValue, newValue) => HashSetPatch.diff(oldValue, newValue), patch: (patch, oldValue) => HashSetPatch.patch(oldValue)(patch) }) /** @internal */ export const orElseEither = Dual.dual< (that: D.Differ) => ( self: D.Differ ) => D.Differ, D.Differ.Or.Patch>, ( self: D.Differ, that: D.Differ ) => D.Differ, D.Differ.Or.Patch> >(2, (self, that) => make({ empty: OrPatch.empty(), combine: (first, second) => OrPatch.combine(first, second), diff: (oldValue, newValue) => OrPatch.diff({ oldValue, newValue, left: self, right: that }), patch: (patch, oldValue) => OrPatch.patch(patch, { oldValue, left: self, right: that }) })) /** @internal */ export const transform = Dual.dual< ( options: { readonly toNew: (value: Value) => Value2 readonly toOld: (value: Value2) => Value } ) => (self: D.Differ) => D.Differ, ( self: D.Differ, options: { readonly toNew: (value: Value) => Value2 readonly toOld: (value: Value2) => Value } ) => D.Differ >(2, (self, { toNew, toOld }) => make({ empty: self.empty, combine: (first, second) => self.combine(first, second), diff: (oldValue, newValue) => self.diff(toOld(oldValue), toOld(newValue)), patch: (patch, oldValue) => toNew(self.patch(patch, toOld(oldValue))) })) /** @internal */ export const update = (): D.Differ A> => updateWith((_, a) => a) /** @internal */ export const updateWith = (f: (x: A, y: A) => A): D.Differ A> => make({ empty: identity, combine: (first, second) => { if (first === identity) { return second } if (second === identity) { return first } return (a) => second(first(a)) }, diff: (oldValue, newValue) => { if (Equal.equals(oldValue, newValue)) { return identity } return constant(newValue) }, patch: (patch, oldValue) => f(oldValue, patch(oldValue)) }) /** @internal */ export const zip = Dual.dual< (that: D.Differ) => ( self: D.Differ ) => D.Differ, ( self: D.Differ, that: D.Differ ) => D.Differ >(2, (self, that) => make({ empty: [self.empty, that.empty] as const, combine: (first, second) => [ self.combine(first[0], second[0]), that.combine(first[1], second[1]) ], diff: (oldValue, newValue) => [ self.diff(oldValue[0], newValue[0]), that.diff(oldValue[1], newValue[1]) ], patch: (patch, oldValue) => [ self.patch(patch[0], oldValue[0]), that.patch(patch[1], oldValue[1]) ] }))