98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
import { Embeddable, GenericEmbedded, isEmbedded } from "./embedded";
|
|
import { Bytes } from "./bytes";
|
|
import { Record, Tuple } from "./record";
|
|
import { Value } from "./values";
|
|
import { Dictionary, KeyedDictionary, Set } from "./dictionary";
|
|
import { JsDictionary } from "./jsdictionary";
|
|
|
|
export interface FromJSOptions<T extends Embeddable = GenericEmbedded> {
|
|
onNonInteger?(n: number): Value<T> | undefined;
|
|
}
|
|
|
|
export function fromJS<T extends Embeddable = GenericEmbedded>(x: any): Value<T> {
|
|
return fromJS_options(x);
|
|
}
|
|
|
|
export function fromJS_options<T extends Embeddable = GenericEmbedded>(x: any, options?: FromJSOptions<T>): Value<T> {
|
|
function walk(x: any): Value<T> {
|
|
switch (typeof x) {
|
|
case 'number':
|
|
if (!Number.isInteger(x)) {
|
|
// We require that clients be explicit about integer vs. non-integer types.
|
|
const converted = options?.onNonInteger?.(x) ?? void 0;
|
|
if (converted !== void 0) return converted;
|
|
throw new TypeError("Refusing to autoconvert non-integer number to Double");
|
|
}
|
|
// FALL THROUGH
|
|
case 'bigint':
|
|
case 'string':
|
|
case 'symbol':
|
|
case 'boolean':
|
|
return x;
|
|
|
|
case 'undefined':
|
|
case 'function':
|
|
break;
|
|
|
|
case 'object':
|
|
if (x === null) {
|
|
break;
|
|
}
|
|
if (typeof x.__as_preserve__ === 'function') {
|
|
return x.__as_preserve__();
|
|
}
|
|
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(x)) {
|
|
return x;
|
|
}
|
|
if (Array.isArray(x)) {
|
|
return x.map<Value<T>>(walk);
|
|
}
|
|
if (ArrayBuffer.isView(x) || x instanceof ArrayBuffer) {
|
|
return Bytes.from(x);
|
|
}
|
|
if (Map.isMap(x)) {
|
|
const d = new KeyedDictionary<T>();
|
|
x.forEach((v, k) => d.set(walk(k), walk(v)));
|
|
return d;
|
|
}
|
|
if (Set.isSet(x)) {
|
|
const s = new Set<T>();
|
|
x.forEach(v => s.add(walk(v)));
|
|
return s;
|
|
}
|
|
if (isEmbedded<T>(x)) {
|
|
return x;
|
|
}
|
|
// Handle plain JS objects to build a JsDictionary
|
|
{
|
|
const r: JsDictionary<Value<T>> = {};
|
|
Object.entries(x).forEach(([k, v]) => r[k] = walk(v));
|
|
return r;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
throw new TypeError("Cannot represent JavaScript value as Preserves: " + x);
|
|
}
|
|
|
|
return walk(x);
|
|
}
|
|
|
|
declare module "./dictionary" {
|
|
namespace Dictionary {
|
|
export function stringMap<T extends Embeddable = GenericEmbedded>(
|
|
x: object
|
|
): KeyedDictionary<T, string, Value<T>>;
|
|
}
|
|
}
|
|
|
|
Dictionary.stringMap = function <T extends Embeddable = GenericEmbedded>(
|
|
x: object
|
|
): KeyedDictionary<T, string, Value<T>> {
|
|
const r = new KeyedDictionary<T, string, Value<T>>();
|
|
Object.entries(x).forEach(([key, value]) => r.set(key, fromJS(value)));
|
|
return r;
|
|
};
|