/** * @since 2.0.0 */ import type { Equivalence } from "./Equivalence.js" import * as Hash from "./Hash.js" import { hasProperty } from "./Predicate.js" import { structuralRegionState } from "./Utils.js" /** * @since 2.0.0 * @category symbols */ export const symbol: unique symbol = Symbol.for("effect/Equal") /** * @since 2.0.0 * @category models */ export interface Equal extends Hash.Hash { [symbol](that: Equal): boolean } /** * @since 2.0.0 * @category equality */ export function equals(that: B): (self: A) => boolean export function equals(self: A, that: B): boolean export function equals(): any { if (arguments.length === 1) { return (self: unknown) => compareBoth(self, arguments[0]) } return compareBoth(arguments[0], arguments[1]) } function compareBoth(self: unknown, that: unknown): boolean { if (self === that) { return true } const selfType = typeof self if (selfType !== typeof that) { return false } if (selfType === "object" || selfType === "function") { if (self !== null && that !== null) { if (isEqual(self) && isEqual(that)) { if (Hash.hash(self) === Hash.hash(that) && self[symbol](that)) { return true } else { return structuralRegionState.enabled && structuralRegionState.tester ? structuralRegionState.tester(self, that) : false } } else if (self instanceof Date && that instanceof Date) { return self.toISOString() === that.toISOString() } else if (self instanceof URL && that instanceof URL) { return self.href === that.href } } if (structuralRegionState.enabled) { if (Array.isArray(self) && Array.isArray(that)) { return self.length === that.length && self.every((v, i) => compareBoth(v, that[i])) } if (Object.getPrototypeOf(self) === Object.prototype && Object.getPrototypeOf(self) === Object.prototype) { const keysSelf = Object.keys(self as any) const keysThat = Object.keys(that as any) if (keysSelf.length === keysThat.length) { for (const key of keysSelf) { // @ts-expect-error if (!(key in that && compareBoth(self[key], that[key]))) { return structuralRegionState.tester ? structuralRegionState.tester(self, that) : false } } return true } } return structuralRegionState.tester ? structuralRegionState.tester(self, that) : false } } return structuralRegionState.enabled && structuralRegionState.tester ? structuralRegionState.tester(self, that) : false } /** * @since 2.0.0 * @category guards */ export const isEqual = (u: unknown): u is Equal => hasProperty(u, symbol) /** * @since 2.0.0 * @category instances */ export const equivalence: () => Equivalence = () => equals