diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-12-16 12:53:22 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-12-16 12:53:22 +0100 |
commit | fa4621e70c48500a372504eb8ae9b9481531c555 (patch) | |
tree | 50c457c8c2133dfec32cb465e1b3902ce88fb209 /src/util | |
parent | 1b9c5855a8afb6833ff7a706f5bed5650e1191ad (diff) |
history events WIP
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/amounts.ts | 7 | ||||
-rw-r--r-- | src/util/codec-test.ts | 26 | ||||
-rw-r--r-- | src/util/codec.ts | 159 | ||||
-rw-r--r-- | src/util/helpers.ts | 10 | ||||
-rw-r--r-- | src/util/query.ts | 11 |
5 files changed, 121 insertions, 92 deletions
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<AmountJson> = objectCodec<AmountJson>() - .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<MyObj>() - .property("foo", stringCodec) + const myObjCodec = makeCodecForObject<MyObj>() + .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<AltOne> = objectCodec<AltOne>() - .property("type", stringConstCodec("one")) - .property("foo", stringCodec) + const altOneCodec: Codec<AltOne> = makeCodecForObject<AltOne>() + .property("type", makeCodecForConstString("one")) + .property("foo", codecForString) .build("AltOne"); - const altTwoCodec: Codec<AltTwo> = objectCodec<AltTwo>() - .property("type", stringConstCodec("two")) - .property("bar", stringCodec) + const altTwoCodec: Codec<AltTwo> = makeCodecForObject<AltTwo>() + .property("type", makeCodecForConstString("two")) + .property("bar", codecForString) .build("AltTwo"); - const myUnionCodec: Codec<MyUnion> = unionCodec<MyUnion>() + const myUnionCodec: Codec<MyUnion> = makeCodecForUnion<MyUnion>() .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<any>; } -class ObjectCodecBuilder<T, TC> { +class ObjectCodecBuilder<OutputType, PartialOutputType> { private propList: Prop[] = []; /** * Define a property for the object. */ - property<K extends keyof T & string, V extends T[K]>( + property<K extends keyof OutputType & string, V extends OutputType[K]>( x: K, codec: Codec<V>, - ): ObjectCodecBuilder<T, TC & SingletonRecord<K, V>> { + ): ObjectCodecBuilder<OutputType, PartialOutputType & SingletonRecord<K, V>> { this.propList.push({ name: x, codec: codec }); return this as any; } @@ -94,10 +94,10 @@ class ObjectCodecBuilder<T, TC> { * @param objectDisplayName name of the object that this codec operates on, * used in error messages. */ - build(objectDisplayName: string): Codec<TC> { + build(objectDisplayName: string): Codec<PartialOutputType> { 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<T, TC> { ); obj[prop.name] = propVal; } - return obj as TC; + return obj as PartialOutputType; }, }; } } -class UnionCodecBuilder<T, D extends keyof T, B, TC> { +class UnionCodecBuilder< + TargetType, + TagPropertyLabel extends keyof TargetType, + CommonBaseType, + PartialTargetType +> { private alternatives = new Map<any, Alternative>(); - constructor(private discriminator: D, private baseCodec?: Codec<B>) {} + constructor( + private discriminator: TagPropertyLabel, + private baseCodec?: Codec<CommonBaseType>, + ) {} /** * Define a property for the object. */ alternative<V>( - tagValue: T[D], + tagValue: TargetType[TagPropertyLabel], codec: Codec<V>, - ): UnionCodecBuilder<T, D, B, TC | V> { + ): UnionCodecBuilder< + TargetType, + TagPropertyLabel, + CommonBaseType, + PartialTargetType | V + > { this.alternatives.set(tagValue, { codec, tagValue }); return this as any; } @@ -140,7 +153,9 @@ class UnionCodecBuilder<T, D extends keyof T, B, TC> { * @param objectDisplayName name of the object that this codec operates on, * used in error messages. */ - build<R extends TC & B>(objectDisplayName: string): Codec<R> { + build<R extends PartialTargetType & CommonBaseType = never>( + objectDisplayName: string, + ): Codec<R> { const alternatives = this.alternatives; const discriminator = this.discriminator; const baseCodec = this.baseCodec; @@ -174,50 +189,50 @@ class UnionCodecBuilder<T, D extends keyof T, B, TC> { } } +export class UnionCodecPreBuilder<T> { + discriminateOn<D extends keyof T, B = {}>( + discriminator: D, + baseCodec?: Codec<B>, + ): UnionCodecBuilder<T, D, B, never> { + return new UnionCodecBuilder<T, D, B, never>(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<string> = { - decode(x: any, c?: Context): string { - if (typeof x === "string") { - return x; - } - throw new DecodingError(`expected string at ${renderContext(c)}`); - }, -}; +export function makeCodecForObject<T>(): ObjectCodecBuilder<T, {}> { + return new ObjectCodecBuilder<T, {}>(); +} + +export function makeCodecForUnion<T>(): UnionCodecPreBuilder<T> { + return new UnionCodecPreBuilder<T>(); +} /** - * 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<V extends string>(s: V): Codec<V> { +export function makeCodecForMap<T>( + innerCodec: Codec<T>, +): 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<number> = { - 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<T>(innerCodec: Codec<T>): Codec<T[]> { +export function makeCodecForList<T>(innerCodec: Codec<T>): Codec<T[]> { return { decode(x: any, c?: Context): T[] { const arr: T[] = []; @@ -233,39 +248,45 @@ export function listCodec<T>(innerCodec: Codec<T>): Codec<T[]> { } /** - * 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<T>(innerCodec: Codec<T>): 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<number> = { + decode(x: any, c?: Context): number { + if (typeof x === "number") { + return x; + } + throw new DecodingError(`expected number at ${renderContext(c)}`); + }, +}; -export class UnionCodecPreBuilder<T> { - discriminateOn<D extends keyof T, B>( - discriminator: D, - baseCodec?: Codec<B>, - ): UnionCodecBuilder<T, D, B, never> { - return new UnionCodecBuilder<T, D, B, never>(discriminator, baseCodec); - } -} +/** + * Return a codec for a value that must be a string. + */ +export const codecForString: Codec<string> = { + 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<T>(): ObjectCodecBuilder<T, {}> { - return new ObjectCodecBuilder<T, {}>(); +export function makeCodecForConstString<V extends string>(s: V): Codec<V> { + 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<T>(): UnionCodecPreBuilder<T> { - return new UnionCodecPreBuilder<T>(); +export function typecheckedCodec<T = undefined>(c: Codec<T>): Codec<T> { + 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<T>(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<T> { return arr; } + async forEachAsync(f: (x: T) => Promise<void>): Promise<void> { + while (true) { + const x = await this.next(); + if (x.hasValue) { + await f(x.value); + } else { + break; + } + } + } + async forEach(f: (x: T) => void): Promise<void> { while (true) { const x = await this.next(); |