import type { Chunk } from "../Chunk.js" import type { Context } from "../Context.js" import type * as Differ from "../Differ.js" import type { Either } from "../Either.js" import * as Equal from "../Equal.js" import * as Dual from "../Function.js" import { constant, identity } from "../Function.js" import type { HashMap } from "../HashMap.js" import type { HashSet } from "../HashSet.js" import { pipeArguments } from "../Pipeable.js" import * as ChunkPatch from "./differ/chunkPatch.js" import * as ContextPatch from "./differ/contextPatch.js" import * as HashMapPatch from "./differ/hashMapPatch.js" import * as HashSetPatch from "./differ/hashSetPatch.js" import * as OrPatch from "./differ/orPatch.js" import * as ReadonlyArrayPatch from "./differ/readonlyArrayPatch.js" /** @internal */ export const DifferTypeId: Differ.TypeId = Symbol.for("effect/Differ") as Differ.TypeId /** @internal */ export const DifferProto = { [DifferTypeId]: { _P: identity, _V: identity }, pipe() { return pipeArguments(this, arguments) } } /** @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 } ): Differ.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 = (): Differ.Differ, Differ.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: Differ.Differ ): Differ.Differ, Differ.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: Differ.Differ ): Differ.Differ, Differ.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 = (): Differ.Differ, Differ.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: Differ.Differ) => ( self: Differ.Differ ) => Differ.Differ, Differ.Differ.Or.Patch>, ( self: Differ.Differ, that: Differ.Differ ) => Differ.Differ, Differ.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 readonlyArray = ( differ: Differ.Differ ): Differ.Differ, Differ.Differ.ReadonlyArray.Patch> => make({ empty: ReadonlyArrayPatch.empty(), combine: (first, second) => ReadonlyArrayPatch.combine(first, second), diff: (oldValue, newValue) => ReadonlyArrayPatch.diff({ oldValue, newValue, differ }), patch: (patch, oldValue) => ReadonlyArrayPatch.patch(patch, oldValue, differ) }) /** @internal */ export const transform = Dual.dual< ( options: { readonly toNew: (value: Value) => Value2 readonly toOld: (value: Value2) => Value } ) => (self: Differ.Differ) => Differ.Differ, ( self: Differ.Differ, options: { readonly toNew: (value: Value) => Value2 readonly toOld: (value: Value2) => Value } ) => Differ.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 = (): Differ.Differ A> => updateWith((_, a) => a) /** @internal */ export const updateWith = (f: (x: A, y: A) => A): Differ.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: Differ.Differ) => ( self: Differ.Differ ) => Differ.Differ, ( self: Differ.Differ, that: Differ.Differ ) => Differ.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]) ] }))