UNPKG

veffect

Version:

powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha

312 lines (285 loc) 7.92 kB
import * as Chunk from "../../Chunk.js" import type { Differ } from "../../Differ.js" import type { Either } from "../../Either.js" import * as E from "../../Either.js" import * as Equal from "../../Equal.js" import * as Dual from "../../Function.js" import { Structural } from "../data.js" /** @internal */ export const OrPatchTypeId: Differ.Or.TypeId = Symbol.for("effect/DifferOrPatch") as Differ.Or.TypeId function variance<A, B>(a: A): B { return a as unknown as B } /** @internal */ const PatchProto = { ...Structural.prototype, [OrPatchTypeId]: { _Value: variance, _Key: variance, _Patch: variance } } /** @internal */ export interface Empty<in out Value, in out Value2, in out Patch, in out Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "Empty" } const EmptyProto = Object.assign(Object.create(PatchProto), { _tag: "Empty" }) const _empty = Object.create(EmptyProto) /** @internal */ export const empty = <Value, Value2, Patch, Patch2>(): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => _empty /** @internal */ export interface AndThen<in out Value, in out Value2, in out Patch, Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "AndThen" readonly first: Differ.Or.Patch<Value, Value2, Patch, Patch2> readonly second: Differ.Or.Patch<Value, Value2, Patch, Patch2> } const AndThenProto = Object.assign(Object.create(PatchProto), { _tag: "AndThen" }) /** @internal */ export const makeAndThen = <Value, Value2, Patch, Patch2>( first: Differ.Or.Patch<Value, Value2, Patch, Patch2>, second: Differ.Or.Patch<Value, Value2, Patch, Patch2> ): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => { const o = Object.create(AndThenProto) o.first = first o.second = second return o } /** @internal */ export interface SetLeft<in out Value, in out Value2, in out Patch, Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "SetLeft" readonly value: Value } const SetLeftProto = Object.assign(Object.create(PatchProto), { _tag: "SetLeft" }) /** @internal */ export const makeSetLeft = <Value, Value2, Patch, Patch2>( value: Value ): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => { const o = Object.create(SetLeftProto) o.value = value return o } /** @internal */ export interface SetRight<in out Value, in out Value2, in out Patch, in out Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "SetRight" readonly value: Value2 } const SetRightProto = Object.assign(Object.create(PatchProto), { _tag: "SetRight" }) /** @internal */ export const makeSetRight = <Value, Value2, Patch, Patch2>( value: Value2 ): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => { const o = Object.create(SetRightProto) o.value = value return o } /** @internal */ export interface UpdateLeft<in out Value, in out Value2, in out Patch, in out Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "UpdateLeft" readonly patch: Patch } const UpdateLeftProto = Object.assign(Object.create(PatchProto), { _tag: "UpdateLeft" }) /** @internal */ export const makeUpdateLeft = <Value, Value2, Patch, Patch2>( patch: Patch ): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => { const o = Object.create(UpdateLeftProto) o.patch = patch return o } /** @internal */ export interface UpdateRight<in out Value, in out Value2, in out Patch, in out Patch2> extends Differ.Or.Patch<Value, Value2, Patch, Patch2> { readonly _tag: "UpdateRight" readonly patch: Patch2 } const UpdateRightProto = Object.assign(Object.create(PatchProto), { _tag: "UpdateRight" }) /** @internal */ export const makeUpdateRight = <Value, Value2, Patch, Patch2>( patch: Patch2 ): Differ.Or.Patch< Value, Value2, Patch, Patch2 > => { const o = Object.create(UpdateRightProto) o.patch = patch return o } type Instruction = | AndThen<any, any, any, any> | Empty<any, any, any, any> | SetLeft<any, any, any, any> | SetRight<any, any, any, any> | UpdateLeft<any, any, any, any> | UpdateRight<any, any, any, any> /** @internal */ export const diff = <Value, Value2, Patch, Patch2>( options: { readonly oldValue: Either<Value2, Value> readonly newValue: Either<Value2, Value> readonly left: Differ<Value, Patch> readonly right: Differ<Value2, Patch2> } ): Differ.Or.Patch<Value, Value2, Patch, Patch2> => { switch (options.oldValue._tag) { case "Left": { switch (options.newValue._tag) { case "Left": { const valuePatch = options.left.diff(options.oldValue.left, options.newValue.left) if (Equal.equals(valuePatch, options.left.empty)) { return empty() } return makeUpdateLeft(valuePatch) } case "Right": { return makeSetRight(options.newValue.right) } } } case "Right": { switch (options.newValue._tag) { case "Left": { return makeSetLeft(options.newValue.left) } case "Right": { const valuePatch = options.right.diff(options.oldValue.right, options.newValue.right) if (Equal.equals(valuePatch, options.right.empty)) { return empty() } return makeUpdateRight(valuePatch) } } } } } /** @internal */ export const combine = Dual.dual< <Value, Value2, Patch, Patch2>( that: Differ.Or.Patch<Value, Value2, Patch, Patch2> ) => ( self: Differ.Or.Patch<Value, Value2, Patch, Patch2> ) => Differ.Or.Patch<Value, Value2, Patch, Patch2>, <Value, Value2, Patch, Patch2>( self: Differ.Or.Patch<Value, Value2, Patch, Patch2>, that: Differ.Or.Patch<Value, Value2, Patch, Patch2> ) => Differ.Or.Patch<Value, Value2, Patch, Patch2> >(2, (self, that) => makeAndThen(self, that)) /** @internal */ export const patch = Dual.dual< <Value, Value2, Patch, Patch2>( options: { readonly oldValue: Either<Value2, Value> readonly left: Differ<Value, Patch> readonly right: Differ<Value2, Patch2> } ) => (self: Differ.Or.Patch<Value, Value2, Patch, Patch2>) => Either<Value2, Value>, <Value, Value2, Patch, Patch2>( self: Differ.Or.Patch<Value, Value2, Patch, Patch2>, options: { readonly oldValue: Either<Value2, Value> readonly left: Differ<Value, Patch> readonly right: Differ<Value2, Patch2> } ) => Either<Value2, Value> >(2, <Value, Value2, Patch, Patch2>( self: Differ.Or.Patch<Value, Value2, Patch, Patch2>, { left, oldValue, right }: { oldValue: Either<Value2, Value> left: Differ<Value, Patch> right: Differ<Value2, Patch2> } ) => { if ((self as Instruction)._tag === "Empty") { return oldValue } let patches: Chunk.Chunk<Differ.Or.Patch<Value, Value2, Patch, Patch2>> = Chunk.of(self) let result = oldValue while (Chunk.isNonEmpty(patches)) { const head: Instruction = Chunk.headNonEmpty(patches) as Instruction const tail = Chunk.tailNonEmpty(patches) switch (head._tag) { case "Empty": { patches = tail break } case "AndThen": { patches = Chunk.prepend(head.first)(Chunk.prepend(head.second)(tail)) break } case "UpdateLeft": { if (result._tag === "Left") { result = E.left(left.patch(head.patch, result.left)) } patches = tail break } case "UpdateRight": { if (result._tag === "Right") { result = E.right(right.patch(head.patch, result.right)) } patches = tail break } case "SetLeft": { result = E.left(head.value) patches = tail break } case "SetRight": { result = E.right(head.value) patches = tail break } } } return result })