gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: codecs WIP


From: gnunet
Subject: [taler-wallet-core] branch master updated: codecs WIP
Date: Sat, 14 Dec 2019 17:23:46 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 749b9628 codecs WIP
749b9628 is described below

commit 749b96284ae0a7e6d03034806deab998a36b7cf6
Author: Florian Dold <address@hidden>
AuthorDate: Sat Dec 14 17:23:31 2019 +0100

    codecs WIP
---
 gulpfile.js            |   1 +
 src/util/codec-test.ts |  36 ++++++++++
 src/util/codec.ts      | 181 +++++++++++++++++++++++++++++++++++++++++++++++++
 tsconfig.json          |   3 +
 4 files changed, 221 insertions(+)

diff --git a/gulpfile.js b/gulpfile.js
index dbdb33cc..15f6ff10 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -99,6 +99,7 @@ const tsBaseArgs = {
   strictPropertyInitialization: false,
   outDir: "dist/node",
   noImplicitAny: true,
+  noImplicitThis: true,
   allowJs: true,
   checkJs: true,
   incremental: true,
diff --git a/src/util/codec-test.ts b/src/util/codec-test.ts
new file mode 100644
index 00000000..d7edd545
--- /dev/null
+++ b/src/util/codec-test.ts
@@ -0,0 +1,36 @@
+/*
+ This file is part of GNU Taler
+ (C) 2018-2019 GNUnet e.V.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Type-safe codecs for converting from/to JSON.
+ */
+
+import test from "ava";
+import { stringCodec, objectCodec } from "./codec";
+
+interface MyObj {
+  foo: string;
+}
+
+test("basic codec", (t) => {
+  const myObjCodec = objectCodec<MyObj>().property("foo", 
stringCodec).build("MyObj");
+  const res = myObjCodec.decode({ foo: "hello" });
+  t.assert(res.foo === "hello");
+
+  t.throws(() => {
+    const res2 = myObjCodec.decode({ foo: 123 });
+  });
+});
diff --git a/src/util/codec.ts b/src/util/codec.ts
new file mode 100644
index 00000000..690486b7
--- /dev/null
+++ b/src/util/codec.ts
@@ -0,0 +1,181 @@
+/*
+ This file is part of GNU Taler
+ (C) 2018-2019 GNUnet e.V.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Type-safe codecs for converting from/to JSON.
+ */
+
+/**
+ * Error thrown when decoding fails.
+ */
+export class DecodingError extends Error {
+  constructor(message: string) {
+    super(message);
+    Object.setPrototypeOf(this, DecodingError.prototype);
+    this.name = "DecodingError";
+  }
+}
+
+/**
+ * Context information to show nicer error messages when decoding fails.
+ */
+interface Context {
+  readonly path?: string[];
+}
+
+function renderContext(c?: Context): string {
+  const p = c?.path;
+  if (p) {
+    return p.join(".");
+  } else {
+    return "(unknown)";
+  }
+}
+
+function joinContext(c: Context | undefined, part: string): Context {
+  const path = c?.path ?? [];
+  return {
+    path: path.concat([part]),
+  };
+}
+
+/**
+ * A codec converts untyped JSON to a typed object.
+ */
+export interface Codec<V> {
+  /**
+   * Decode untyped JSON to an object of type [[V]].
+   */
+  readonly decode: (x: any, c?: Context) => V;
+}
+
+type SingletonRecord<K extends keyof any, V> = { [Y in K]: V };
+
+interface Prop {
+  name: string;
+  codec: Codec<any>;
+}
+
+class ObjectCodecBuilder<T, TC> {
+  private propList: Prop[] = [];
+
+  /**
+   * Define a property for the object.
+   */
+  property<K extends keyof T & string, V>(
+    x: K,
+    codec: Codec<V>,
+  ): ObjectCodecBuilder<T, TC & SingletonRecord<K, V>> {
+    this.propList.push({ name: x, codec: codec });
+    return this as any;
+  }
+
+  /**
+   * Return the built codec.
+   *
+   * @param objectDisplayName name of the object that this codec operates on,
+   *   used in error messages.
+   */
+  build(objectDisplayName: string): Codec<TC> {
+    const propList = this.propList;
+    return {
+      decode(x: any, c?: Context): TC {
+        if (!c) {
+          c = {
+            path: [`(${objectDisplayName})`],
+          };
+        }
+        const obj: any = {};
+        for (const prop of propList) {
+          const propRawVal = x[prop.name];
+          const propVal = prop.codec.decode(
+            propRawVal,
+            joinContext(c, prop.name),
+          );
+          obj[prop.name] = propVal;
+        }
+        return obj as TC;
+      },
+    };
+  }
+}
+
+/**
+ * Return a codec for a value that must be a string.
+ */
+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)}`);
+  },
+};
+
+/**
+ * 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[]> {
+  return {
+    decode(x: any, c?: Context): T[] {
+      const arr: T[] = [];
+      if (!Array.isArray(x)) {
+        throw new DecodingError(`expected array at ${renderContext(c)}`);
+      }
+      for (const i in x) {
+        arr.push(innerCodec.decode(x[i], joinContext(c, `[${i}]`)));
+      }
+      return arr;
+    },
+  };
+}
+
+/**
+ * Return a codec for a mapping from a string to values described by the inner 
codec.
+ */
+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;
+    },
+  };
+}
+
+/**
+ * Return a builder for a codec that decodes an object with properties.
+ */
+export function objectCodec<T>(): ObjectCodecBuilder<T, {}> {
+  return new ObjectCodecBuilder<T, {}>();
+}
diff --git a/tsconfig.json b/tsconfig.json
index 8d696591..cb2985ae 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,6 +17,7 @@
     "strictPropertyInitialization": false,
     "outDir": "dist/node",
     "noImplicitAny": true,
+    "noImplicitThis": true,
     "allowJs": true,
     "checkJs": true,
     "incremental": true,
@@ -69,6 +70,8 @@
     "src/util/assertUnreachable.ts",
     "src/util/asyncMemo.ts",
     "src/util/checkable.ts",
+    "src/util/codec-test.ts",
+    "src/util/codec.ts",
     "src/util/helpers-test.ts",
     "src/util/helpers.ts",
     "src/util/http.ts",

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]