aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-12-16 12:53:22 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-12-16 12:53:22 +0100
commitfa4621e70c48500a372504eb8ae9b9481531c555 (patch)
tree50c457c8c2133dfec32cb465e1b3902ce88fb209 /src/util
parent1b9c5855a8afb6833ff7a706f5bed5650e1191ad (diff)
history events WIP
Diffstat (limited to 'src/util')
-rw-r--r--src/util/amounts.ts7
-rw-r--r--src/util/codec-test.ts26
-rw-r--r--src/util/codec.ts159
-rw-r--r--src/util/helpers.ts10
-rw-r--r--src/util/query.ts11
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();