From fa4621e70c48500a372504eb8ae9b9481531c555 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 16 Dec 2019 12:53:22 +0100 Subject: history events WIP --- src/util/amounts.ts | 7 --- src/util/codec-test.ts | 26 ++++---- src/util/codec.ts | 159 ++++++++++++++++++++++++++++--------------------- src/util/helpers.ts | 10 ++++ src/util/query.ts | 11 ++++ 5 files changed, 121 insertions(+), 92 deletions(-) (limited to 'src/util') diff --git a/src/util/amounts.ts b/src/util/amounts.ts index 26cee7f8f..c8fb76793 100644 --- a/src/util/amounts.ts +++ b/src/util/amounts.ts @@ -22,7 +22,6 @@ * Imports. */ import { Checkable } from "./checkable"; -import { objectCodec, numberCodec, stringCodec, Codec } from "./codec"; /** * Number of fractional units that one value unit represents. @@ -68,12 +67,6 @@ export class AmountJson { static checked: (obj: any) => AmountJson; } -const amountJsonCodec: Codec = objectCodec() - .property("value", numberCodec) - .property("fraction", numberCodec) - .property("currency", stringCodec) - .build("AmountJson"); - /** * Result of a possibly overflowing operation. */ diff --git a/src/util/codec-test.ts b/src/util/codec-test.ts index 22f6a0a98..7c7c93c7b 100644 --- a/src/util/codec-test.ts +++ b/src/util/codec-test.ts @@ -19,13 +19,7 @@ */ import test from "ava"; -import { - stringCodec, - objectCodec, - unionCodec, - Codec, - stringConstCodec, -} from "./codec"; +import { Codec, makeCodecForObject, makeCodecForConstString, codecForString, makeCodecForUnion } from "./codec"; interface MyObj { foo: string; @@ -44,8 +38,8 @@ interface AltTwo { type MyUnion = AltOne | AltTwo; test("basic codec", t => { - const myObjCodec = objectCodec() - .property("foo", stringCodec) + const myObjCodec = makeCodecForObject() + .property("foo", codecForString) .build("MyObj"); const res = myObjCodec.decode({ foo: "hello" }); t.assert(res.foo === "hello"); @@ -56,15 +50,15 @@ test("basic codec", t => { }); test("union", t => { - const altOneCodec: Codec = objectCodec() - .property("type", stringConstCodec("one")) - .property("foo", stringCodec) + const altOneCodec: Codec = makeCodecForObject() + .property("type", makeCodecForConstString("one")) + .property("foo", codecForString) .build("AltOne"); - const altTwoCodec: Codec = objectCodec() - .property("type", stringConstCodec("two")) - .property("bar", stringCodec) + const altTwoCodec: Codec = makeCodecForObject() + .property("type", makeCodecForConstString("two")) + .property("bar", codecForString) .build("AltTwo"); - const myUnionCodec: Codec = unionCodec() + const myUnionCodec: Codec = makeCodecForUnion() .discriminateOn("type") .alternative("one", altOneCodec) .alternative("two", altTwoCodec) diff --git a/src/util/codec.ts b/src/util/codec.ts index 0215ce797..a13816c59 100644 --- a/src/util/codec.ts +++ b/src/util/codec.ts @@ -74,16 +74,16 @@ interface Alternative { codec: Codec; } -class ObjectCodecBuilder { +class ObjectCodecBuilder { private propList: Prop[] = []; /** * Define a property for the object. */ - property( + property( x: K, codec: Codec, - ): ObjectCodecBuilder> { + ): ObjectCodecBuilder> { this.propList.push({ name: x, codec: codec }); return this as any; } @@ -94,10 +94,10 @@ class ObjectCodecBuilder { * @param objectDisplayName name of the object that this codec operates on, * used in error messages. */ - build(objectDisplayName: string): Codec { + build(objectDisplayName: string): Codec { const propList = this.propList; return { - decode(x: any, c?: Context): TC { + decode(x: any, c?: Context): PartialOutputType { if (!c) { c = { path: [`(${objectDisplayName})`], @@ -112,24 +112,37 @@ class ObjectCodecBuilder { ); obj[prop.name] = propVal; } - return obj as TC; + return obj as PartialOutputType; }, }; } } -class UnionCodecBuilder { +class UnionCodecBuilder< + TargetType, + TagPropertyLabel extends keyof TargetType, + CommonBaseType, + PartialTargetType +> { private alternatives = new Map(); - constructor(private discriminator: D, private baseCodec?: Codec) {} + constructor( + private discriminator: TagPropertyLabel, + private baseCodec?: Codec, + ) {} /** * Define a property for the object. */ alternative( - tagValue: T[D], + tagValue: TargetType[TagPropertyLabel], codec: Codec, - ): UnionCodecBuilder { + ): UnionCodecBuilder< + TargetType, + TagPropertyLabel, + CommonBaseType, + PartialTargetType | V + > { this.alternatives.set(tagValue, { codec, tagValue }); return this as any; } @@ -140,7 +153,9 @@ class UnionCodecBuilder { * @param objectDisplayName name of the object that this codec operates on, * used in error messages. */ - build(objectDisplayName: string): Codec { + build( + objectDisplayName: string, + ): Codec { const alternatives = this.alternatives; const discriminator = this.discriminator; const baseCodec = this.baseCodec; @@ -174,50 +189,50 @@ class UnionCodecBuilder { } } +export class UnionCodecPreBuilder { + discriminateOn( + discriminator: D, + baseCodec?: Codec, + ): UnionCodecBuilder { + return new UnionCodecBuilder(discriminator, baseCodec); + } +} + /** - * Return a codec for a value that must be a string. + * Return a builder for a codec that decodes an object with properties. */ -export const stringCodec: Codec = { - decode(x: any, c?: Context): string { - if (typeof x === "string") { - return x; - } - throw new DecodingError(`expected string at ${renderContext(c)}`); - }, -}; +export function makeCodecForObject(): ObjectCodecBuilder { + return new ObjectCodecBuilder(); +} + +export function makeCodecForUnion(): UnionCodecPreBuilder { + return new UnionCodecPreBuilder(); +} /** - * Return a codec for a value that must be a string. + * Return a codec for a mapping from a string to values described by the inner codec. */ -export function stringConstCodec(s: V): Codec { +export function makeCodecForMap( + innerCodec: Codec, +): Codec<{ [x: string]: T }> { return { - decode(x: any, c?: Context): V { - if (x === s) { - return x; + decode(x: any, c?: Context): { [x: string]: T } { + const map: { [x: string]: T } = {}; + if (typeof x !== "object") { + throw new DecodingError(`expected object at ${renderContext(c)}`); } - throw new DecodingError( - `expected string constant "${s}" at ${renderContext(c)}`, - ); + for (const i in x) { + map[i] = innerCodec.decode(x[i], joinContext(c, `[${i}]`)); + } + return map; }, }; } -/** - * Return a codec for a value that must be a number. - */ -export const numberCodec: Codec = { - decode(x: any, c?: Context): number { - if (typeof x === "number") { - return x; - } - throw new DecodingError(`expected number at ${renderContext(c)}`); - }, -}; - /** * Return a codec for a list, containing values described by the inner codec. */ -export function listCodec(innerCodec: Codec): Codec { +export function makeCodecForList(innerCodec: Codec): Codec { return { decode(x: any, c?: Context): T[] { const arr: T[] = []; @@ -233,39 +248,45 @@ export function listCodec(innerCodec: Codec): Codec { } /** - * Return a codec for a mapping from a string to values described by the inner codec. + * Return a codec for a value that must be a number. */ -export function mapCodec(innerCodec: Codec): Codec<{ [x: string]: T }> { - return { - decode(x: any, c?: Context): { [x: string]: T } { - const map: { [x: string]: T } = {}; - if (typeof x !== "object") { - throw new DecodingError(`expected object at ${renderContext(c)}`); - } - for (const i in x) { - map[i] = innerCodec.decode(x[i], joinContext(c, `[${i}]`)); - } - return map; - }, - }; -} +export const codecForNumber: Codec = { + decode(x: any, c?: Context): number { + if (typeof x === "number") { + return x; + } + throw new DecodingError(`expected number at ${renderContext(c)}`); + }, +}; -export class UnionCodecPreBuilder { - discriminateOn( - discriminator: D, - baseCodec?: Codec, - ): UnionCodecBuilder { - return new UnionCodecBuilder(discriminator, baseCodec); - } -} +/** + * Return a codec for a value that must be a string. + */ +export const codecForString: Codec = { + decode(x: any, c?: Context): string { + if (typeof x === "string") { + return x; + } + throw new DecodingError(`expected string at ${renderContext(c)}`); + }, +}; /** - * Return a builder for a codec that decodes an object with properties. + * Return a codec for a value that must be a string. */ -export function objectCodec(): ObjectCodecBuilder { - return new ObjectCodecBuilder(); +export function makeCodecForConstString(s: V): Codec { + return { + decode(x: any, c?: Context): V { + if (x === s) { + return x; + } + throw new DecodingError( + `expected string constant "${s}" at ${renderContext(c)}`, + ); + }, + }; } -export function unionCodec(): UnionCodecPreBuilder { - return new UnionCodecPreBuilder(); +export function typecheckedCodec(c: Codec): Codec { + return c; } diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 99d046f04..8136f44fa 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -214,3 +214,13 @@ export function strcmp(s1: string, s2: string): number { } return 0; } + +/** + * Run a function and return its result. + * + * Used as a nicer-looking way to do immediately invoked function + * expressions (IFFEs). + */ +export function runBlock(f: () => T) { + return f(); +} \ No newline at end of file diff --git a/src/util/query.ts b/src/util/query.ts index 08a8fec02..217c0674e 100644 --- a/src/util/query.ts +++ b/src/util/query.ts @@ -176,6 +176,17 @@ class ResultStream { return arr; } + async forEachAsync(f: (x: T) => Promise): Promise { + while (true) { + const x = await this.next(); + if (x.hasValue) { + await f(x.value); + } else { + break; + } + } + } + async forEach(f: (x: T) => void): Promise { while (true) { const x = await this.next(); -- cgit v1.2.3