preserves/implementations/javascript/packages/core/src/merge.ts

73 lines
2.4 KiB
TypeScript

import { Record, Tuple } from "./record";
import { Bytes } from "./bytes";
import { fold } from "./fold";
import { is } from "./is";
import { Value } from "./values";
import { Set, Dictionary } from "./dictionary";
import { Annotated } from "./annotated";
import { unannotate } from "./strip";
import { embed, isEmbedded, Embedded } from "./embedded";
export function merge<T>(
mergeEmbeddeds: (a: T, b: T) => T | undefined,
item0: Value<T>,
... items: Array<Value<T>>): Value<T>
{
function die(): never {
throw new Error("Cannot merge items");
}
function walk(a: Value<T>, b: Value<T>): Value<T> {
if (a === b) return a;
return fold<T, Value<T>>(a, {
boolean: die,
single(_f: number) { return is(a, b) ? a : die(); },
double(_f: number) { return is(a, b) ? a : die(); },
integer: die,
string: die,
bytes(_b: Bytes) { return is(a, b) ? a : die(); },
symbol: die,
record(r: Record<Value<T>, Tuple<Value<T>>, T>) {
if (!Record.isRecord<Value<T>, Tuple<Value<T>>, T>(b)) die();
return Record(walk(r.label, b.label), walkMany(r, b));
},
array(a: Array<Value<T>>) {
if (!Array.isArray(b) || Record.isRecord(b)) die();
return walkMany(a, b);
},
set(_s: Set<T>) { die(); },
dictionary(d: Dictionary<T>) {
if (!Dictionary.isDictionary<T>(b)) die();
const r = new Dictionary<T>();
d.forEach((av,ak) => {
const bv = b.get(ak);
r.set(ak, bv === void 0 ? av : walk(av, bv));
});
b.forEach((bv, bk) => {
if (!d.has(bk)) r.set(bk, bv);
});
return r;
},
annotated(a: Annotated<T>) {
return walk(a, unannotate(b));
},
embedded(t: Embedded<T>) {
if (!isEmbedded<T>(b)) die();
const r = mergeEmbeddeds(t.embeddedValue, b.embeddedValue);
if (r === void 0) die();
return embed(r);
},
});
}
function walkMany(a: Array<Value<T>>, b: Array<Value<T>>): Array<Value<T>> {
if (a.length !== b.length) die();
return a.map((aa, i) => walk(aa, b[i]));
}
return items.reduce(walk, item0);
}