From 0c9358c1b2bd80e25940022e86bd8daef8184ad7 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 19 Dec 2019 20:42:49 +0100 Subject: new date format, replace checkable annotations with codecs --- src/util/codec.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) (limited to 'src/util/codec.ts') diff --git a/src/util/codec.ts b/src/util/codec.ts index a13816c59..e18a5e74c 100644 --- a/src/util/codec.ts +++ b/src/util/codec.ts @@ -32,11 +32,11 @@ export class DecodingError extends Error { /** * Context information to show nicer error messages when decoding fails. */ -interface Context { +export interface Context { readonly path?: string[]; } -function renderContext(c?: Context): string { +export function renderContext(c?: Context): string { const p = c?.path; if (p) { return p.join("."); @@ -84,6 +84,9 @@ class ObjectCodecBuilder { x: K, codec: Codec, ): ObjectCodecBuilder> { + if (!codec) { + throw Error("inner codec must be defined"); + } this.propList.push({ name: x, codec: codec }); return this as any; } @@ -143,6 +146,9 @@ class UnionCodecBuilder< CommonBaseType, PartialTargetType | V > { + if (!codec) { + throw Error("inner codec must be defined"); + } this.alternatives.set(tagValue, { codec, tagValue }); return this as any; } @@ -215,6 +221,9 @@ export function makeCodecForUnion(): UnionCodecPreBuilder { export function makeCodecForMap( innerCodec: Codec, ): Codec<{ [x: string]: T }> { + if (!innerCodec) { + throw Error("inner codec must be defined"); + } return { decode(x: any, c?: Context): { [x: string]: T } { const map: { [x: string]: T } = {}; @@ -233,6 +242,9 @@ export function makeCodecForMap( * Return a codec for a list, containing values described by the inner codec. */ export function makeCodecForList(innerCodec: Codec): Codec { + if (!innerCodec) { + throw Error("inner codec must be defined"); + } return { decode(x: any, c?: Context): T[] { const arr: T[] = []; @@ -255,7 +267,19 @@ export const codecForNumber: Codec = { if (typeof x === "number") { return x; } - throw new DecodingError(`expected number at ${renderContext(c)}`); + throw new DecodingError(`expected number at ${renderContext(c)} but got ${typeof x}`); + }, +}; + +/** + * Return a codec for a value that must be a number. + */ +export const codecForBoolean: Codec = { + decode(x: any, c?: Context): boolean { + if (typeof x === "boolean") { + return x; + } + throw new DecodingError(`expected boolean at ${renderContext(c)} but got ${typeof x}`); }, }; @@ -267,7 +291,16 @@ export const codecForString: Codec = { if (typeof x === "string") { return x; } - throw new DecodingError(`expected string at ${renderContext(c)}`); + throw new DecodingError(`expected string at ${renderContext(c)} but got ${typeof x}`); + }, +}; + +/** + * Codec that allows any value. + */ +export const codecForAny: Codec = { + decode(x: any, c?: Context): any { + return x; }, }; @@ -281,12 +314,23 @@ export function makeCodecForConstString(s: V): Codec { return x; } throw new DecodingError( - `expected string constant "${s}" at ${renderContext(c)}`, + `expected string constant "${s}" at ${renderContext(c)} but got ${typeof x}`, ); }, }; } +export function makeCodecOptional(innerCodec: Codec): Codec { + return { + decode(x: any, c?: Context): V | undefined { + if (x === undefined || x === null) { + return undefined; + } + return innerCodec.decode(x, c); + } + } +} + export function typecheckedCodec(c: Codec): Codec { return c; } -- cgit v1.2.3