diff options
Diffstat (limited to 'packages/taler-wallet-core/src/util')
22 files changed, 10 insertions, 2504 deletions
diff --git a/packages/taler-wallet-core/src/util/RequestThrottler.ts b/packages/taler-wallet-core/src/util/RequestThrottler.ts index 0bdd7cab7..b38e948fe 100644 --- a/packages/taler-wallet-core/src/util/RequestThrottler.ts +++ b/packages/taler-wallet-core/src/util/RequestThrottler.ts @@ -25,7 +25,7 @@ import { getTimestampNow, timestampDifference, timestampCmp, -} from "../util/time"; +} from "@gnu-taler/taler-util"; import { URL } from "./url"; import { Logger } from "./logging"; diff --git a/packages/taler-wallet-core/src/util/amounts-test.ts b/packages/taler-wallet-core/src/util/amounts-test.ts deleted file mode 100644 index afd8caa51..000000000 --- a/packages/taler-wallet-core/src/util/amounts-test.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - 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/> - */ - -import test from "ava"; - -import { Amounts, AmountJson } from "../util/amounts"; - -const jAmt = ( - value: number, - fraction: number, - currency: string, -): AmountJson => ({ value, fraction, currency }); - -const sAmt = (s: string): AmountJson => Amounts.parseOrThrow(s); - -test("amount addition (simple)", (t) => { - const a1 = jAmt(1, 0, "EUR"); - const a2 = jAmt(1, 0, "EUR"); - const a3 = jAmt(2, 0, "EUR"); - t.true(0 === Amounts.cmp(Amounts.add(a1, a2).amount, a3)); - t.pass(); -}); - -test("amount addition (saturation)", (t) => { - const a1 = jAmt(1, 0, "EUR"); - const res = Amounts.add(jAmt(Amounts.maxAmountValue, 0, "EUR"), a1); - t.true(res.saturated); - t.pass(); -}); - -test("amount subtraction (simple)", (t) => { - const a1 = jAmt(2, 5, "EUR"); - const a2 = jAmt(1, 0, "EUR"); - const a3 = jAmt(1, 5, "EUR"); - t.true(0 === Amounts.cmp(Amounts.sub(a1, a2).amount, a3)); - t.pass(); -}); - -test("amount subtraction (saturation)", (t) => { - const a1 = jAmt(0, 0, "EUR"); - const a2 = jAmt(1, 0, "EUR"); - let res = Amounts.sub(a1, a2); - t.true(res.saturated); - res = Amounts.sub(a1, a1); - t.true(!res.saturated); - t.pass(); -}); - -test("amount comparison", (t) => { - t.is(Amounts.cmp(jAmt(1, 0, "EUR"), jAmt(1, 0, "EUR")), 0); - t.is(Amounts.cmp(jAmt(1, 1, "EUR"), jAmt(1, 0, "EUR")), 1); - t.is(Amounts.cmp(jAmt(1, 1, "EUR"), jAmt(1, 2, "EUR")), -1); - t.is(Amounts.cmp(jAmt(1, 0, "EUR"), jAmt(0, 0, "EUR")), 1); - t.is(Amounts.cmp(jAmt(0, 0, "EUR"), jAmt(1, 0, "EUR")), -1); - t.is(Amounts.cmp(jAmt(1, 0, "EUR"), jAmt(0, 100000000, "EUR")), 0); - t.throws(() => Amounts.cmp(jAmt(1, 0, "FOO"), jAmt(1, 0, "BAR"))); - t.pass(); -}); - -test("amount parsing", (t) => { - t.is( - Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:0"), jAmt(0, 0, "TESTKUDOS")), - 0, - ); - t.is( - Amounts.cmp(Amounts.parseOrThrow("TESTKUDOS:10"), jAmt(10, 0, "TESTKUDOS")), - 0, - ); - t.is( - Amounts.cmp( - Amounts.parseOrThrow("TESTKUDOS:0.1"), - jAmt(0, 10000000, "TESTKUDOS"), - ), - 0, - ); - t.is( - Amounts.cmp( - Amounts.parseOrThrow("TESTKUDOS:0.00000001"), - jAmt(0, 1, "TESTKUDOS"), - ), - 0, - ); - t.is( - Amounts.cmp( - Amounts.parseOrThrow("TESTKUDOS:4503599627370496.99999999"), - jAmt(4503599627370496, 99999999, "TESTKUDOS"), - ), - 0, - ); - t.throws(() => Amounts.parseOrThrow("foo:")); - t.throws(() => Amounts.parseOrThrow("1.0")); - t.throws(() => Amounts.parseOrThrow("42")); - t.throws(() => Amounts.parseOrThrow(":1.0")); - t.throws(() => Amounts.parseOrThrow(":42")); - t.throws(() => Amounts.parseOrThrow("EUR:.42")); - t.throws(() => Amounts.parseOrThrow("EUR:42.")); - t.throws(() => Amounts.parseOrThrow("TESTKUDOS:4503599627370497.99999999")); - t.is( - Amounts.cmp( - Amounts.parseOrThrow("TESTKUDOS:0.99999999"), - jAmt(0, 99999999, "TESTKUDOS"), - ), - 0, - ); - t.throws(() => Amounts.parseOrThrow("TESTKUDOS:0.999999991")); - t.pass(); -}); - -test("amount stringification", (t) => { - t.is(Amounts.stringify(jAmt(0, 0, "TESTKUDOS")), "TESTKUDOS:0"); - t.is(Amounts.stringify(jAmt(4, 94000000, "TESTKUDOS")), "TESTKUDOS:4.94"); - t.is(Amounts.stringify(jAmt(0, 10000000, "TESTKUDOS")), "TESTKUDOS:0.1"); - t.is(Amounts.stringify(jAmt(0, 1, "TESTKUDOS")), "TESTKUDOS:0.00000001"); - t.is(Amounts.stringify(jAmt(5, 0, "TESTKUDOS")), "TESTKUDOS:5"); - // denormalized - t.is(Amounts.stringify(jAmt(1, 100000000, "TESTKUDOS")), "TESTKUDOS:2"); - t.pass(); -}); - -test("amount multiplication", (t) => { - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 0).amount), "EUR:0"); - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 1).amount), "EUR:1.11"); - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 2).amount), "EUR:2.22"); - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 3).amount), "EUR:3.33"); - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 4).amount), "EUR:4.44"); - t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 5).amount), "EUR:5.55"); -}); diff --git a/packages/taler-wallet-core/src/util/amounts.ts b/packages/taler-wallet-core/src/util/amounts.ts deleted file mode 100644 index 7a242f41d..000000000 --- a/packages/taler-wallet-core/src/util/amounts.ts +++ /dev/null @@ -1,423 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 Taler Systems S.A. - - 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/> - */ - -/** - * Types and helper functions for dealing with Taler amounts. - */ - -/** - * Imports. - */ -import { - buildCodecForObject, - codecForString, - codecForNumber, - Codec, -} from "./codec"; -import { AmountString } from "../types/talerTypes"; - -/** - * Number of fractional units that one value unit represents. - */ -export const fractionalBase = 1e8; - -/** - * How many digits behind the comma are required to represent the - * fractional value in human readable decimal format? Must match - * lg(fractionalBase) - */ -export const fractionalLength = 8; - -/** - * Maximum allowed value field of an amount. - */ -export const maxAmountValue = 2 ** 52; - -/** - * Non-negative financial amount. Fractional values are expressed as multiples - * of 1e-8. - */ -export interface AmountJson { - /** - * Value, must be an integer. - */ - readonly value: number; - - /** - * Fraction, must be an integer. Represent 1/1e8 of a unit. - */ - readonly fraction: number; - - /** - * Currency of the amount. - */ - readonly currency: string; -} - -export const codecForAmountJson = (): Codec<AmountJson> => - buildCodecForObject<AmountJson>() - .property("currency", codecForString()) - .property("value", codecForNumber()) - .property("fraction", codecForNumber()) - .build("AmountJson"); - -export const codecForAmountString = (): Codec<AmountString> => codecForString(); - -/** - * Result of a possibly overflowing operation. - */ -export interface Result { - /** - * Resulting, possibly saturated amount. - */ - amount: AmountJson; - /** - * Was there an over-/underflow? - */ - saturated: boolean; -} - -/** - * Get an amount that represents zero units of a currency. - */ -export function getZero(currency: string): AmountJson { - return { - currency, - fraction: 0, - value: 0, - }; -} - -export type AmountLike = AmountString | AmountJson; - -export function jsonifyAmount(amt: AmountLike): AmountJson { - if (typeof amt === "string") { - return parseOrThrow(amt); - } - return amt; -} - -export function sum(amounts: AmountLike[]): Result { - if (amounts.length <= 0) { - throw Error("can't sum zero amounts"); - } - const jsonAmounts = amounts.map((x) => jsonifyAmount(x)); - return add(jsonAmounts[0], ...jsonAmounts.slice(1)); -} - -/** - * Add two amounts. Return the result and whether - * the addition overflowed. The overflow is always handled - * by saturating and never by wrapping. - * - * Throws when currencies don't match. - */ -export function add(first: AmountJson, ...rest: AmountJson[]): Result { - const currency = first.currency; - let value = first.value + Math.floor(first.fraction / fractionalBase); - if (value > maxAmountValue) { - return { - amount: { currency, value: maxAmountValue, fraction: fractionalBase - 1 }, - saturated: true, - }; - } - let fraction = first.fraction % fractionalBase; - for (const x of rest) { - if (x.currency !== currency) { - throw Error(`Mismatched currency: ${x.currency} and ${currency}`); - } - - value = - value + x.value + Math.floor((fraction + x.fraction) / fractionalBase); - fraction = Math.floor((fraction + x.fraction) % fractionalBase); - if (value > maxAmountValue) { - return { - amount: { - currency, - value: maxAmountValue, - fraction: fractionalBase - 1, - }, - saturated: true, - }; - } - } - return { amount: { currency, value, fraction }, saturated: false }; -} - -/** - * Subtract two amounts. Return the result and whether - * the subtraction overflowed. The overflow is always handled - * by saturating and never by wrapping. - * - * Throws when currencies don't match. - */ -export function sub(a: AmountJson, ...rest: AmountJson[]): Result { - const currency = a.currency; - let value = a.value; - let fraction = a.fraction; - - for (const b of rest) { - if (b.currency !== currency) { - throw Error(`Mismatched currency: ${b.currency} and ${currency}`); - } - if (fraction < b.fraction) { - if (value < 1) { - return { amount: { currency, value: 0, fraction: 0 }, saturated: true }; - } - value--; - fraction += fractionalBase; - } - console.assert(fraction >= b.fraction); - fraction -= b.fraction; - if (value < b.value) { - return { amount: { currency, value: 0, fraction: 0 }, saturated: true }; - } - value -= b.value; - } - - return { amount: { currency, value, fraction }, saturated: false }; -} - -/** - * Compare two amounts. Returns 0 when equal, -1 when a < b - * and +1 when a > b. Throws when currencies don't match. - */ -export function cmp(a: AmountLike, b: AmountLike): -1 | 0 | 1 { - a = jsonifyAmount(a); - b = jsonifyAmount(b); - if (a.currency !== b.currency) { - throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`); - } - const av = a.value + Math.floor(a.fraction / fractionalBase); - const af = a.fraction % fractionalBase; - const bv = b.value + Math.floor(b.fraction / fractionalBase); - const bf = b.fraction % fractionalBase; - switch (true) { - case av < bv: - return -1; - case av > bv: - return 1; - case af < bf: - return -1; - case af > bf: - return 1; - case af === bf: - return 0; - default: - throw Error("assertion failed"); - } -} - -/** - * Create a copy of an amount. - */ -export function copy(a: AmountJson): AmountJson { - return { - currency: a.currency, - fraction: a.fraction, - value: a.value, - }; -} - -/** - * Divide an amount. Throws on division by zero. - */ -export function divide(a: AmountJson, n: number): AmountJson { - if (n === 0) { - throw Error(`Division by 0`); - } - if (n === 1) { - return { value: a.value, fraction: a.fraction, currency: a.currency }; - } - const r = a.value % n; - return { - currency: a.currency, - fraction: Math.floor((r * fractionalBase + a.fraction) / n), - value: Math.floor(a.value / n), - }; -} - -/** - * Check if an amount is non-zero. - */ -export function isNonZero(a: AmountJson): boolean { - return a.value > 0 || a.fraction > 0; -} - -export function isZero(a: AmountLike): boolean { - a = jsonifyAmount(a); - return a.value === 0 && a.fraction === 0; -} - -/** - * Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct. - */ -export function parse(s: string): AmountJson | undefined { - const res = s.match(/^([a-zA-Z0-9_*-]+):([0-9]+)([.][0-9]+)?$/); - if (!res) { - return undefined; - } - const tail = res[3] || ".0"; - if (tail.length > fractionalLength + 1) { - return undefined; - } - const value = Number.parseInt(res[2]); - if (value > maxAmountValue) { - return undefined; - } - return { - currency: res[1], - fraction: Math.round(fractionalBase * Number.parseFloat(tail)), - value, - }; -} - -/** - * Parse amount in standard string form (like 'EUR:20.5'), - * throw if the input is not a valid amount. - */ -export function parseOrThrow(s: string): AmountJson { - const res = parse(s); - if (!res) { - throw Error(`Can't parse amount: "${s}"`); - } - return res; -} - -/** - * Convert a float to a Taler amount. - * Loss of precision possible. - */ -export function fromFloat(floatVal: number, currency: string): AmountJson { - return { - currency, - fraction: Math.floor((floatVal - Math.floor(floatVal)) * fractionalBase), - value: Math.floor(floatVal), - }; -} - -/** - * Convert to standard human-readable string representation that's - * also used in JSON formats. - */ -export function stringify(a: AmountLike): string { - a = jsonifyAmount(a); - const av = a.value + Math.floor(a.fraction / fractionalBase); - const af = a.fraction % fractionalBase; - let s = av.toString(); - - if (af) { - s = s + "."; - let n = af; - for (let i = 0; i < fractionalLength; i++) { - if (!n) { - break; - } - s = s + Math.floor((n / fractionalBase) * 10).toString(); - n = (n * 10) % fractionalBase; - } - } - - return `${a.currency}:${s}`; -} - -/** - * Check if the argument is a valid amount in string form. - */ -function check(a: any): boolean { - if (typeof a !== "string") { - return false; - } - try { - const parsedAmount = parse(a); - return !!parsedAmount; - } catch { - return false; - } -} - -function mult(a: AmountJson, n: number): Result { - if (!Number.isInteger(n)) { - throw Error("amount can only be multipied by an integer"); - } - if (n < 0) { - throw Error("amount can only be multiplied by a positive integer"); - } - if (n == 0) { - return { amount: getZero(a.currency), saturated: false }; - } - let x = a; - let acc = getZero(a.currency); - while (n > 1) { - if (n % 2 == 0) { - n = n / 2; - } else { - n = (n - 1) / 2; - const r2 = add(acc, x); - if (r2.saturated) { - return r2; - } - acc = r2.amount; - } - const r2 = add(x, x); - if (r2.saturated) { - return r2; - } - x = r2.amount; - } - return add(acc, x); -} - -function max(a: AmountLike, b: AmountLike): AmountJson { - const cr = Amounts.cmp(a, b); - if (cr >= 0) { - return jsonifyAmount(a); - } else { - return jsonifyAmount(b); - } -} - -function min(a: AmountLike, b: AmountLike): AmountJson { - const cr = Amounts.cmp(a, b); - if (cr >= 0) { - return jsonifyAmount(b); - } else { - return jsonifyAmount(a); - } -} - - -// Export all amount-related functions here for better IDE experience. -export const Amounts = { - stringify: stringify, - parse: parse, - parseOrThrow: parseOrThrow, - cmp: cmp, - add: add, - sum: sum, - sub: sub, - mult: mult, - max: max, - min: min, - check: check, - getZero: getZero, - isZero: isZero, - maxAmountValue: maxAmountValue, - fromFloat: fromFloat, - copy: copy, - fractionalBase: fractionalBase, - divide: divide, -}; diff --git a/packages/taler-wallet-core/src/util/codec-test.ts b/packages/taler-wallet-core/src/util/codec-test.ts deleted file mode 100644 index f8f4c797c..000000000 --- a/packages/taler-wallet-core/src/util/codec-test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - 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 { - Codec, - buildCodecForObject, - codecForConstString, - codecForString, - buildCodecForUnion, -} from "./codec"; - -interface MyObj { - foo: string; -} - -interface AltOne { - type: "one"; - foo: string; -} - -interface AltTwo { - type: "two"; - bar: string; -} - -type MyUnion = AltOne | AltTwo; - -test("basic codec", (t) => { - const myObjCodec = buildCodecForObject<MyObj>() - .property("foo", codecForString()) - .build("MyObj"); - const res = myObjCodec.decode({ foo: "hello" }); - t.assert(res.foo === "hello"); - - t.throws(() => { - myObjCodec.decode({ foo: 123 }); - }); -}); - -test("union", (t) => { - const altOneCodec: Codec<AltOne> = buildCodecForObject<AltOne>() - .property("type", codecForConstString("one")) - .property("foo", codecForString()) - .build("AltOne"); - const altTwoCodec: Codec<AltTwo> = buildCodecForObject<AltTwo>() - .property("type", codecForConstString("two")) - .property("bar", codecForString()) - .build("AltTwo"); - const myUnionCodec: Codec<MyUnion> = buildCodecForUnion<MyUnion>() - .discriminateOn("type") - .alternative("one", altOneCodec) - .alternative("two", altTwoCodec) - .build<MyUnion>("MyUnion"); - - const res = myUnionCodec.decode({ type: "one", foo: "bla" }); - t.is(res.type, "one"); - if (res.type == "one") { - t.is(res.foo, "bla"); - } -}); diff --git a/packages/taler-wallet-core/src/util/codec.ts b/packages/taler-wallet-core/src/util/codec.ts deleted file mode 100644 index 8605ff335..000000000 --- a/packages/taler-wallet-core/src/util/codec.ts +++ /dev/null @@ -1,419 +0,0 @@ -/* - 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. - */ - -/* eslint-disable @typescript-eslint/ban-types */ - -/** - * 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. - */ -export interface Context { - readonly path?: string[]; -} - -export 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>; -} - -interface Alternative { - tagValue: any; - codec: Codec<any>; -} - -class ObjectCodecBuilder<OutputType, PartialOutputType> { - private propList: Prop[] = []; - - /** - * Define a property for the object. - */ - property<K extends keyof OutputType & string, V extends OutputType[K]>( - x: K, - codec: Codec<V>, - ): ObjectCodecBuilder<OutputType, PartialOutputType & SingletonRecord<K, V>> { - if (!codec) { - throw Error("inner codec must be defined"); - } - 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<PartialOutputType> { - const propList = this.propList; - return { - decode(x: any, c?: Context): PartialOutputType { - if (!c) { - c = { - path: [`(${objectDisplayName})`], - }; - } - if (typeof x !== "object") { - throw new DecodingError( - `expected object for ${objectDisplayName} at ${renderContext( - c, - )} but got ${typeof x}`, - ); - } - 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 PartialOutputType; - }, - }; - } -} - -class UnionCodecBuilder< - TargetType, - TagPropertyLabel extends keyof TargetType, - CommonBaseType, - PartialTargetType -> { - private alternatives = new Map<any, Alternative>(); - - constructor( - private discriminator: TagPropertyLabel, - private baseCodec?: Codec<CommonBaseType>, - ) {} - - /** - * Define a property for the object. - */ - alternative<V>( - tagValue: TargetType[TagPropertyLabel], - codec: Codec<V>, - ): UnionCodecBuilder< - TargetType, - TagPropertyLabel, - CommonBaseType, - PartialTargetType | V - > { - if (!codec) { - throw Error("inner codec must be defined"); - } - this.alternatives.set(tagValue, { codec, tagValue }); - return this as any; - } - - /** - * Return the built codec. - * - * @param objectDisplayName name of the object that this codec operates on, - * used in error messages. - */ - build<R extends PartialTargetType & CommonBaseType = never>( - objectDisplayName: string, - ): Codec<R> { - const alternatives = this.alternatives; - const discriminator = this.discriminator; - const baseCodec = this.baseCodec; - return { - decode(x: any, c?: Context): R { - if (!c) { - c = { - path: [`(${objectDisplayName})`], - }; - } - const d = x[discriminator]; - if (d === undefined) { - throw new DecodingError( - `expected tag for ${objectDisplayName} at ${renderContext( - c, - )}.${discriminator}`, - ); - } - const alt = alternatives.get(d); - if (!alt) { - throw new DecodingError( - `unknown tag for ${objectDisplayName} ${d} at ${renderContext( - c, - )}.${discriminator}`, - ); - } - const altDecoded = alt.codec.decode(x); - if (baseCodec) { - const baseDecoded = baseCodec.decode(x, c); - return { ...baseDecoded, ...altDecoded }; - } else { - return altDecoded; - } - }, - }; - } -} - -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 builder for a codec that decodes an object with properties. - */ -export function buildCodecForObject<T>(): ObjectCodecBuilder<T, {}> { - return new ObjectCodecBuilder<T, {}>(); -} - -export function buildCodecForUnion<T>(): UnionCodecPreBuilder<T> { - return new UnionCodecPreBuilder<T>(); -} - -/** - * Return a codec for a mapping from a string to values described by the inner codec. - */ -export function codecForMap<T>( - innerCodec: Codec<T>, -): 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 } = {}; - 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 codec for a list, containing values described by the inner codec. - */ -export function codecForList<T>(innerCodec: Codec<T>): Codec<T[]> { - if (!innerCodec) { - throw Error("inner codec must be defined"); - } - 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 value that must be a number. - */ -export function codecForNumber(): Codec<number> { - return { - decode(x: any, c?: Context): number { - if (typeof x === "number") { - return x; - } - throw new DecodingError( - `expected number at ${renderContext(c)} but got ${typeof x}`, - ); - }, - }; -} - -/** - * Return a codec for a value that must be a number. - */ -export function codecForBoolean(): Codec<boolean> { - return { - decode(x: any, c?: Context): boolean { - if (typeof x === "boolean") { - return x; - } - throw new DecodingError( - `expected boolean at ${renderContext(c)} but got ${typeof x}`, - ); - }, - }; -} - -/** - * Return a codec for a value that must be a string. - */ -export function codecForString(): Codec<string> { - return { - decode(x: any, c?: Context): string { - if (typeof x === "string") { - return x; - } - throw new DecodingError( - `expected string at ${renderContext(c)} but got ${typeof x}`, - ); - }, - }; -} - -/** - * Codec that allows any value. - */ -export function codecForAny(): Codec<any> { - return { - decode(x: any, c?: Context): any { - return x; - }, - }; -} - -/** - * Return a codec for a value that must be a string. - */ -export function codecForConstString<V extends string>(s: V): Codec<V> { - return { - decode(x: any, c?: Context): V { - if (x === s) { - return x; - } - if (typeof x !== "string") { - throw new DecodingError( - `expected string constant "${s}" at ${renderContext( - c, - )} but got ${typeof x}`, - ); - } - throw new DecodingError( - `expected string constant "${s}" at ${renderContext( - c, - )} but got string value "${x}"`, - ); - }, - }; -} - -/** - * Return a codec for a boolean true constant. - */ -export function codecForConstTrue(): Codec<true> { - return { - decode(x: any, c?: Context): true { - if (x === true) { - return x; - } - throw new DecodingError( - `expected boolean true at ${renderContext(c)} but got ${typeof x}`, - ); - }, - }; -} - -/** - * Return a codec for a boolean true constant. - */ -export function codecForConstFalse(): Codec<false> { - return { - decode(x: any, c?: Context): false { - if (x === false) { - return x; - } - throw new DecodingError( - `expected boolean false at ${renderContext(c)} but got ${typeof x}`, - ); - }, - }; -} - -/** - * Return a codec for a value that must be a constant number. - */ -export function codecForConstNumber<V extends number>(n: V): Codec<V> { - return { - decode(x: any, c?: Context): V { - if (x === n) { - return x; - } - throw new DecodingError( - `expected number constant "${n}" at ${renderContext( - c, - )} but got ${typeof x}`, - ); - }, - }; -} - -export function codecOptional<V>(innerCodec: Codec<V>): Codec<V | undefined> { - return { - decode(x: any, c?: Context): V | undefined { - if (x === undefined || x === null) { - return undefined; - } - return innerCodec.decode(x, c); - }, - }; -} diff --git a/packages/taler-wallet-core/src/util/coinSelection-test.ts b/packages/taler-wallet-core/src/util/coinSelection-test.ts index 3ac718aaf..962a14835 100644 --- a/packages/taler-wallet-core/src/util/coinSelection-test.ts +++ b/packages/taler-wallet-core/src/util/coinSelection-test.ts @@ -17,8 +17,8 @@ /** * Imports. */ +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import test from "ava"; -import { AmountJson, Amounts } from ".."; import { AvailableCoinInfo, selectPayCoins } from "./coinSelection"; function a(x: string): AmountJson { diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index d32ab6f3c..e9cec6144 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -23,7 +23,7 @@ /** * Imports. */ -import { AmountJson, Amounts } from "./amounts"; +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import { strcmp } from "./helpers"; /** diff --git a/packages/taler-wallet-core/src/util/helpers.ts b/packages/taler-wallet-core/src/util/helpers.ts index f5c204310..36ecc83fe 100644 --- a/packages/taler-wallet-core/src/util/helpers.ts +++ b/packages/taler-wallet-core/src/util/helpers.ts @@ -21,8 +21,7 @@ /** * Imports. */ -import { AmountJson } from "./amounts"; -import * as Amounts from "./amounts"; +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import { URL } from "./url"; /** diff --git a/packages/taler-wallet-core/src/util/http.ts b/packages/taler-wallet-core/src/util/http.ts index 73f08d407..ee6ff80a2 100644 --- a/packages/taler-wallet-core/src/util/http.ts +++ b/packages/taler-wallet-core/src/util/http.ts @@ -24,19 +24,18 @@ /** * Imports */ -import { Codec } from "./codec"; import { OperationFailedError, makeErrorDetails } from "../operations/errors"; -import { TalerErrorCode } from "../TalerErrorCode"; import { Logger } from "./logging"; import { Duration, Timestamp, getTimestampNow, timestampAddDuration, - timestampMin, timestampMax, -} from "./time"; -import { TalerErrorDetails } from ".."; + TalerErrorDetails, + Codec, +} from "@gnu-taler/taler-util"; +import { TalerErrorCode } from "@gnu-taler/taler-util"; const logger = new Logger("http.ts"); diff --git a/packages/taler-wallet-core/src/util/libtoolVersion-test.ts b/packages/taler-wallet-core/src/util/libtoolVersion-test.ts deleted file mode 100644 index e58e94759..000000000 --- a/packages/taler-wallet-core/src/util/libtoolVersion-test.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of TALER - (C) 2017 GNUnet e.V. - - 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. - - 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import * as LibtoolVersion from "./libtoolVersion"; - -import test from "ava"; - -test("version comparison", (t) => { - t.deepEqual(LibtoolVersion.compare("0:0:0", "0:0:0"), { - compatible: true, - currentCmp: 0, - }); - t.deepEqual(LibtoolVersion.compare("0:0:0", ""), undefined); - t.deepEqual(LibtoolVersion.compare("foo", "0:0:0"), undefined); - t.deepEqual(LibtoolVersion.compare("0:0:0", "1:0:1"), { - compatible: true, - currentCmp: -1, - }); - t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:1"), { - compatible: true, - currentCmp: -1, - }); - t.deepEqual(LibtoolVersion.compare("0:0:0", "1:5:0"), { - compatible: false, - currentCmp: -1, - }); - t.deepEqual(LibtoolVersion.compare("1:0:0", "0:5:0"), { - compatible: false, - currentCmp: 1, - }); - t.deepEqual(LibtoolVersion.compare("1:0:1", "1:5:1"), { - compatible: true, - currentCmp: 0, - }); -}); diff --git a/packages/taler-wallet-core/src/util/libtoolVersion.ts b/packages/taler-wallet-core/src/util/libtoolVersion.ts deleted file mode 100644 index 5e9d0b74e..000000000 --- a/packages/taler-wallet-core/src/util/libtoolVersion.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - This file is part of TALER - (C) 2017 GNUnet e.V. - - 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. - - 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Semantic versioning, but libtool-style. - * See https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html - */ - -/** - * Result of comparing two libtool versions. - */ -export interface VersionMatchResult { - /** - * Is the first version compatible with the second? - */ - compatible: boolean; - /** - * Is the first version older (-1), newser (+1) or - * identical (0)? - */ - currentCmp: number; -} - -interface Version { - current: number; - revision: number; - age: number; -} - -/** - * Compare two libtool-style version strings. - */ -export function compare( - me: string, - other: string, -): VersionMatchResult | undefined { - const meVer = parseVersion(me); - const otherVer = parseVersion(other); - - if (!(meVer && otherVer)) { - return undefined; - } - - const compatible = - meVer.current - meVer.age <= otherVer.current && - meVer.current >= otherVer.current - otherVer.age; - - const currentCmp = Math.sign(meVer.current - otherVer.current); - - return { compatible, currentCmp }; -} - -function parseVersion(v: string): Version | undefined { - const [currentStr, revisionStr, ageStr, ...rest] = v.split(":"); - if (rest.length !== 0) { - return undefined; - } - const current = Number.parseInt(currentStr); - const revision = Number.parseInt(revisionStr); - const age = Number.parseInt(ageStr); - - if (Number.isNaN(current)) { - return undefined; - } - - if (Number.isNaN(revision)) { - return undefined; - } - - if (Number.isNaN(age)) { - return undefined; - } - - return { current, revision, age }; -} diff --git a/packages/taler-wallet-core/src/util/payto-test.ts b/packages/taler-wallet-core/src/util/payto-test.ts deleted file mode 100644 index 01280b650..000000000 --- a/packages/taler-wallet-core/src/util/payto-test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - This file is part of GNU Taler - (C) 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/> - */ - -import test from "ava"; - -import { parsePaytoUri } from "./payto"; - -test("basic payto parsing", (t) => { - const r1 = parsePaytoUri("https://example.com/"); - t.is(r1, undefined); - - const r2 = parsePaytoUri("payto:blabla"); - t.is(r2, undefined); - - const r3 = parsePaytoUri("payto://x-taler-bank/123"); - t.is(r3?.targetType, "x-taler-bank"); - t.is(r3?.targetPath, "123"); -}); diff --git a/packages/taler-wallet-core/src/util/payto.ts b/packages/taler-wallet-core/src/util/payto.ts deleted file mode 100644 index a1c47eb2f..000000000 --- a/packages/taler-wallet-core/src/util/payto.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of GNU Taler - (C) 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/> - */ - -import { URLSearchParams } from "./url"; - -interface PaytoUri { - targetType: string; - targetPath: string; - params: { [name: string]: string }; -} - -const paytoPfx = "payto://"; - -/** - * Add query parameters to a payto URI - */ -export function addPaytoQueryParams( - s: string, - params: { [name: string]: string }, -): string { - const [acct, search] = s.slice(paytoPfx.length).split("?"); - const searchParams = new URLSearchParams(search || ""); - for (const k of Object.keys(params)) { - searchParams.set(k, params[k]); - } - return paytoPfx + acct + "?" + searchParams.toString(); -} - -export function parsePaytoUri(s: string): PaytoUri | undefined { - if (!s.startsWith(paytoPfx)) { - return undefined; - } - - const [acct, search] = s.slice(paytoPfx.length).split("?"); - - const firstSlashPos = acct.indexOf("/"); - - if (firstSlashPos === -1) { - return undefined; - } - - const targetType = acct.slice(0, firstSlashPos); - const targetPath = acct.slice(firstSlashPos + 1); - - const params: { [k: string]: string } = {}; - - const searchParams = new URLSearchParams(search || ""); - - searchParams.forEach((v, k) => { - params[v] = k; - }); - - return { - targetPath, - targetType, - params, - }; -} diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 8be78cfc8..54bb0b2ee 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -21,7 +21,7 @@ /** * Imports. */ -import { Timestamp, Duration, getTimestampNow } from "./time"; +import { Timestamp, Duration, getTimestampNow } from "@gnu-taler/taler-util"; export interface RetryInfo { firstTry: Timestamp; diff --git a/packages/taler-wallet-core/src/util/talerconfig-test.ts b/packages/taler-wallet-core/src/util/talerconfig-test.ts deleted file mode 100644 index 2f920fccf..000000000 --- a/packages/taler-wallet-core/src/util/talerconfig-test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - 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/> - */ - -/** - * Imports - */ -import test from "ava"; -import { pathsub, Configuration } from "./talerconfig"; - -test("pathsub", (t) => { - t.assert("foo" === pathsub("foo", () => undefined)); - - t.assert("fo${bla}o" === pathsub("fo${bla}o", () => undefined)); - - const d: Record<string, string> = { - w: "world", - f: "foo", - "1foo": "x", - foo_bar: "quux", - }; - - t.is( - pathsub("hello ${w}!", (v) => d[v]), - "hello world!", - ); - - t.is( - pathsub("hello ${w} ${w}!", (v) => d[v]), - "hello world world!", - ); - - t.is( - pathsub("hello ${x:-blabla}!", (v) => d[v]), - "hello blabla!", - ); - - // No braces - t.is( - pathsub("hello $w!", (v) => d[v]), - "hello world!", - ); - t.is( - pathsub("hello $foo!", (v) => d[v]), - "hello $foo!", - ); - t.is( - pathsub("hello $1foo!", (v) => d[v]), - "hello $1foo!", - ); - t.is( - pathsub("hello $$ world!", (v) => d[v]), - "hello $$ world!", - ); - t.is( - pathsub("hello $$ world!", (v) => d[v]), - "hello $$ world!", - ); - - t.is( - pathsub("hello $foo_bar!", (v) => d[v]), - "hello quux!", - ); - - // Recursive lookup in default - t.is( - pathsub("hello ${x:-${w}}!", (v) => d[v]), - "hello world!", - ); - - // No variables in variable name part - t.is( - pathsub("hello ${${w}:-x}!", (v) => d[v]), - "hello ${${w}:-x}!", - ); - - // Missing closing brace - t.is( - pathsub("hello ${w!", (v) => d[v]), - "hello ${w!", - ); -}); - -test("path expansion", (t) => { - const config = new Configuration(); - config.setString("paths", "taler_home", "foo/bar"); - config.setString( - "paths", - "taler_data_home", - "$TALER_HOME/.local/share/taler/", - ); - config.setString( - "exchange", - "master_priv_file", - "${TALER_DATA_HOME}/exchange/offline-keys/master.priv", - ); - t.is( - config.getPath("exchange", "MaStER_priv_file").required(), - "foo/bar/.local/share/taler//exchange/offline-keys/master.priv", - ); -}); - -test("recursive path resolution", (t) => { - console.log("recursive test"); - const config = new Configuration(); - config.setString("paths", "a", "x${b}"); - config.setString("paths", "b", "y${a}"); - config.setString("foo", "x", "z${a}"); - t.throws(() => { - config.getPath("foo", "a").required(); - }); -}); diff --git a/packages/taler-wallet-core/src/util/talerconfig.ts b/packages/taler-wallet-core/src/util/talerconfig.ts deleted file mode 100644 index fa8c2d40f..000000000 --- a/packages/taler-wallet-core/src/util/talerconfig.ts +++ /dev/null @@ -1,318 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - 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/> - */ - -/** - * Utilities to handle Taler-style configuration files. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports - */ -import { AmountJson } from "./amounts"; -import * as Amounts from "./amounts"; -import fs from "fs"; - -export class ConfigError extends Error { - constructor(message: string) { - super(); - Object.setPrototypeOf(this, ConfigError.prototype); - this.name = "ConfigError"; - this.message = message; - } -} - -type OptionMap = { [optionName: string]: string }; -type SectionMap = { [sectionName: string]: OptionMap }; - -export class ConfigValue<T> { - constructor( - private sectionName: string, - private optionName: string, - private val: string | undefined, - private converter: (x: string) => T, - ) {} - - required(): T { - if (!this.val) { - throw new ConfigError( - `required option [${this.sectionName}]/${this.optionName} not found`, - ); - } - return this.converter(this.val); - } - - orUndefined(): T | undefined { - if (this.val !== undefined) { - return this.converter(this.val); - } else { - return undefined; - } - } - - orDefault(v: T): T | undefined { - if (this.val !== undefined) { - return this.converter(this.val); - } else { - return v; - } - } - - isDefined(): boolean { - return this.val !== undefined; - } -} - -/** - * Shell-style path substitution. - * - * Supported patterns: - * "$x" (look up "x") - * "${x}" (look up "x") - * "${x:-y}" (look up "x", fall back to expanded y) - */ -export function pathsub( - x: string, - lookup: (s: string, depth: number) => string | undefined, - depth = 0, -): string { - if (depth >= 10) { - throw Error("recursion in path substitution"); - } - let s = x; - let l = 0; - while (l < s.length) { - if (s[l] === "$") { - if (s[l + 1] === "{") { - let depth = 1; - const start = l; - let p = start + 2; - let insideNamePart = true; - let hasDefault = false; - for (; p < s.length; p++) { - if (s[p] == "}") { - insideNamePart = false; - depth--; - } else if (s[p] === "$" && s[p + 1] === "{") { - insideNamePart = false; - depth++; - } - if (insideNamePart && s[p] === ":" && s[p + 1] === "-") { - hasDefault = true; - } - if (depth == 0) { - break; - } - } - if (depth == 0) { - const inner = s.slice(start + 2, p); - let varname: string; - let defaultValue: string | undefined; - if (hasDefault) { - [varname, defaultValue] = inner.split(":-", 2); - } else { - varname = inner; - defaultValue = undefined; - } - - const r = lookup(inner, depth + 1); - if (r !== undefined) { - s = s.substr(0, start) + r + s.substr(p + 1); - l = start + r.length; - continue; - } else if (defaultValue !== undefined) { - const resolvedDefault = pathsub(defaultValue, lookup, depth + 1); - s = s.substr(0, start) + resolvedDefault + s.substr(p + 1); - l = start + resolvedDefault.length; - continue; - } - } - l = p; - continue; - } else { - const m = /^[a-zA-Z-_][a-zA-Z0-9-_]*/.exec(s.substring(l + 1)); - if (m && m[0]) { - const r = lookup(m[0], depth + 1); - if (r !== undefined) { - s = s.substr(0, l) + r + s.substr(l + 1 + m[0].length); - l = l + r.length; - continue; - } - } - } - } - l++; - } - return s; -} - -export class Configuration { - private sectionMap: SectionMap = {}; - - loadFromString(s: string): void { - const reComment = /^\s*#.*$/; - const reSection = /^\s*\[\s*([^\]]*)\s*\]\s*$/; - const reParam = /^\s*([^=]+?)\s*=\s*(.*?)\s*$/; - const reEmptyLine = /^\s*$/; - - let currentSection: string | undefined = undefined; - - const lines = s.split("\n"); - for (const line of lines) { - if (reEmptyLine.test(line)) { - continue; - } - if (reComment.test(line)) { - continue; - } - const secMatch = line.match(reSection); - if (secMatch) { - currentSection = secMatch[1]; - continue; - } - if (currentSection === undefined) { - throw Error("invalid configuration, expected section header"); - } - currentSection = currentSection.toUpperCase(); - const paramMatch = line.match(reParam); - if (paramMatch) { - const optName = paramMatch[1].toUpperCase(); - let val = paramMatch[2]; - if (val.startsWith('"') && val.endsWith('"')) { - val = val.slice(1, val.length - 1); - } - const sec = this.sectionMap[currentSection] ?? {}; - this.sectionMap[currentSection] = Object.assign(sec, { - [optName]: val, - }); - continue; - } - throw Error( - "invalid configuration, expected section header or option assignment", - ); - } - } - - setString(section: string, option: string, value: string): void { - const secNorm = section.toUpperCase(); - const sec = this.sectionMap[secNorm] ?? (this.sectionMap[secNorm] = {}); - sec[option.toUpperCase()] = value; - } - - /** - * Get lower-cased section names. - */ - getSectionNames(): string[] { - return Object.keys(this.sectionMap).map((x) => x.toLowerCase()); - } - - getString(section: string, option: string): ConfigValue<string> { - const secNorm = section.toUpperCase(); - const optNorm = option.toUpperCase(); - const val = (this.sectionMap[secNorm] ?? {})[optNorm]; - return new ConfigValue(secNorm, optNorm, val, (x) => x); - } - - getPath(section: string, option: string): ConfigValue<string> { - const secNorm = section.toUpperCase(); - const optNorm = option.toUpperCase(); - const val = (this.sectionMap[secNorm] ?? {})[optNorm]; - return new ConfigValue(secNorm, optNorm, val, (x) => - pathsub(x, (v, d) => this.lookupVariable(v, d + 1)), - ); - } - - getYesNo(section: string, option: string): ConfigValue<boolean> { - const secNorm = section.toUpperCase(); - const optNorm = option.toUpperCase(); - const val = (this.sectionMap[secNorm] ?? {})[optNorm]; - const convert = (x: string): boolean => { - x = x.toLowerCase(); - if (x === "yes") { - return true; - } else if (x === "no") { - return false; - } - throw Error( - `invalid config value for [${secNorm}]/${optNorm}, expected yes/no`, - ); - }; - return new ConfigValue(secNorm, optNorm, val, convert); - } - - getNumber(section: string, option: string): ConfigValue<number> { - const secNorm = section.toUpperCase(); - const optNorm = option.toUpperCase(); - const val = (this.sectionMap[secNorm] ?? {})[optNorm]; - const convert = (x: string): number => { - try { - return Number.parseInt(x, 10); - } catch (e) { - throw Error( - `invalid config value for [${secNorm}]/${optNorm}, expected number`, - ); - } - }; - return new ConfigValue(secNorm, optNorm, val, convert); - } - - lookupVariable(x: string, depth: number = 0): string | undefined { - // We loop up options in PATHS in upper case, as option names - // are case insensitive - const val = (this.sectionMap["PATHS"] ?? {})[x.toUpperCase()]; - if (val !== undefined) { - return pathsub(val, (v, d) => this.lookupVariable(v, d), depth); - } - // Environment variables can be case sensitive, respect that. - const envVal = process.env[x]; - if (envVal !== undefined) { - return envVal; - } - return; - } - - getAmount(section: string, option: string): ConfigValue<AmountJson> { - const val = (this.sectionMap[section] ?? {})[option]; - return new ConfigValue(section, option, val, (x) => - Amounts.parseOrThrow(x), - ); - } - - static load(filename: string): Configuration { - const s = fs.readFileSync(filename, "utf-8"); - const cfg = new Configuration(); - cfg.loadFromString(s); - return cfg; - } - - write(filename: string): void { - let s = ""; - for (const sectionName of Object.keys(this.sectionMap)) { - s += `[${sectionName}]\n`; - for (const optionName of Object.keys( - this.sectionMap[sectionName] ?? {}, - )) { - const val = this.sectionMap[sectionName][optionName]; - if (val !== undefined) { - s += `${optionName} = ${val}\n`; - } - } - s += "\n"; - } - fs.writeFileSync(filename, s); - } -} diff --git a/packages/taler-wallet-core/src/util/taleruri-test.ts b/packages/taler-wallet-core/src/util/taleruri-test.ts deleted file mode 100644 index e80acc5c3..000000000 --- a/packages/taler-wallet-core/src/util/taleruri-test.ts +++ /dev/null @@ -1,184 +0,0 @@ -/* - This file is part of GNU Taler - (C) 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/> - */ - -import test from "ava"; -import { - parsePayUri, - parseWithdrawUri, - parseRefundUri, - parseTipUri, -} from "./taleruri"; - -test("taler pay url parsing: wrong scheme", (t) => { - const url1 = "talerfoo://"; - const r1 = parsePayUri(url1); - t.is(r1, undefined); - - const url2 = "taler://refund/a/b/c/d/e/f"; - const r2 = parsePayUri(url2); - t.is(r2, undefined); -}); - -test("taler pay url parsing: defaults", (t) => { - const url1 = "taler://pay/example.com/myorder/"; - const r1 = parsePayUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://example.com/"); - t.is(r1.sessionId, ""); - - const url2 = "taler://pay/example.com/myorder/mysession"; - const r2 = parsePayUri(url2); - if (!r2) { - t.fail(); - return; - } - t.is(r2.merchantBaseUrl, "https://example.com/"); - t.is(r2.sessionId, "mysession"); -}); - -test("taler pay url parsing: instance", (t) => { - const url1 = "taler://pay/example.com/instances/myinst/myorder/"; - const r1 = parsePayUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://example.com/instances/myinst/"); - t.is(r1.orderId, "myorder"); -}); - -test("taler pay url parsing (claim token)", (t) => { - const url1 = "taler://pay/example.com/instances/myinst/myorder/?c=ASDF"; - const r1 = parsePayUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://example.com/instances/myinst/"); - t.is(r1.orderId, "myorder"); - t.is(r1.claimToken, "ASDF"); -}); - -test("taler refund uri parsing: non-https #1", (t) => { - const url1 = "taler+http://refund/example.com/myorder/"; - const r1 = parseRefundUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "http://example.com/"); - t.is(r1.orderId, "myorder"); -}); - -test("taler pay uri parsing: non-https", (t) => { - const url1 = "taler+http://pay/example.com/myorder/"; - const r1 = parsePayUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "http://example.com/"); - t.is(r1.orderId, "myorder"); -}); - -test("taler pay uri parsing: missing session component", (t) => { - const url1 = "taler+http://pay/example.com/myorder"; - const r1 = parsePayUri(url1); - if (r1) { - t.fail(); - return; - } - t.pass(); -}); - -test("taler withdraw uri parsing", (t) => { - const url1 = "taler://withdraw/bank.example.com/12345"; - const r1 = parseWithdrawUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.withdrawalOperationId, "12345"); - t.is(r1.bankIntegrationApiBaseUrl, "https://bank.example.com/"); -}); - -test("taler withdraw uri parsing (http)", (t) => { - const url1 = "taler+http://withdraw/bank.example.com/12345"; - const r1 = parseWithdrawUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.withdrawalOperationId, "12345"); - t.is(r1.bankIntegrationApiBaseUrl, "http://bank.example.com/"); -}); - -test("taler refund uri parsing", (t) => { - const url1 = "taler://refund/merchant.example.com/1234/"; - const r1 = parseRefundUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://merchant.example.com/"); - t.is(r1.orderId, "1234"); -}); - -test("taler refund uri parsing with instance", (t) => { - const url1 = "taler://refund/merchant.example.com/instances/myinst/1234/"; - const r1 = parseRefundUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.orderId, "1234"); - t.is(r1.merchantBaseUrl, "https://merchant.example.com/instances/myinst/"); -}); - -test("taler tip pickup uri", (t) => { - const url1 = "taler://tip/merchant.example.com/tipid"; - const r1 = parseTipUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://merchant.example.com/"); -}); - -test("taler tip pickup uri with instance", (t) => { - const url1 = "taler://tip/merchant.example.com/instances/tipm/tipid"; - const r1 = parseTipUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://merchant.example.com/instances/tipm/"); - t.is(r1.merchantTipId, "tipid"); -}); - -test("taler tip pickup uri with instance and prefix", (t) => { - const url1 = "taler://tip/merchant.example.com/my/pfx/tipm/tipid"; - const r1 = parseTipUri(url1); - if (!r1) { - t.fail(); - return; - } - t.is(r1.merchantBaseUrl, "https://merchant.example.com/my/pfx/tipm/"); - t.is(r1.merchantTipId, "tipid"); -}); diff --git a/packages/taler-wallet-core/src/util/taleruri.ts b/packages/taler-wallet-core/src/util/taleruri.ts deleted file mode 100644 index d8366fd0f..000000000 --- a/packages/taler-wallet-core/src/util/taleruri.ts +++ /dev/null @@ -1,220 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019-2020 Taler Systems S.A. - - 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/> - */ - -import { canonicalizeBaseUrl } from "./helpers"; -import { URLSearchParams } from "./url"; - -export interface PayUriResult { - merchantBaseUrl: string; - orderId: string; - sessionId: string; - claimToken: string | undefined; -} - -export interface WithdrawUriResult { - bankIntegrationApiBaseUrl: string; - withdrawalOperationId: string; -} - -export interface RefundUriResult { - merchantBaseUrl: string; - orderId: string; -} - -export interface TipUriResult { - merchantTipId: string; - merchantBaseUrl: string; -} - -/** - * Parse a taler[+http]://withdraw URI. - * Return undefined if not passed a valid URI. - */ -export function parseWithdrawUri(s: string): WithdrawUriResult | undefined { - const pi = parseProtoInfo(s, "withdraw"); - if (!pi) { - return undefined; - } - const parts = pi.rest.split("/"); - - if (parts.length < 2) { - return undefined; - } - - const host = parts[0].toLowerCase(); - const pathSegments = parts.slice(1, parts.length - 1); - const withdrawId = parts[parts.length - 1]; - const p = [host, ...pathSegments].join("/"); - - return { - bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`), - withdrawalOperationId: withdrawId, - }; -} - -export enum TalerUriType { - TalerPay = "taler-pay", - TalerWithdraw = "taler-withdraw", - TalerTip = "taler-tip", - TalerRefund = "taler-refund", - TalerNotifyReserve = "taler-notify-reserve", - Unknown = "unknown", -} - -/** - * Classify a taler:// URI. - */ -export function classifyTalerUri(s: string): TalerUriType { - const sl = s.toLowerCase(); - if (sl.startsWith("taler://pay/")) { - return TalerUriType.TalerPay; - } - if (sl.startsWith("taler+http://pay/")) { - return TalerUriType.TalerPay; - } - if (sl.startsWith("taler://tip/")) { - return TalerUriType.TalerTip; - } - if (sl.startsWith("taler+http://tip/")) { - return TalerUriType.TalerTip; - } - if (sl.startsWith("taler://refund/")) { - return TalerUriType.TalerRefund; - } - if (sl.startsWith("taler+http://refund/")) { - return TalerUriType.TalerRefund; - } - if (sl.startsWith("taler://withdraw/")) { - return TalerUriType.TalerWithdraw; - } - if (sl.startsWith("taler+http://withdraw/")) { - return TalerUriType.TalerWithdraw; - } - if (sl.startsWith("taler://notify-reserve/")) { - return TalerUriType.TalerNotifyReserve; - } - return TalerUriType.Unknown; -} - -interface TalerUriProtoInfo { - innerProto: "http" | "https"; - rest: string; -} - -function parseProtoInfo( - s: string, - action: string, -): TalerUriProtoInfo | undefined { - const pfxPlain = `taler://${action}/`; - const pfxHttp = `taler+http://${action}/`; - if (s.toLowerCase().startsWith(pfxPlain)) { - return { - innerProto: "https", - rest: s.substring(pfxPlain.length), - }; - } else if (s.toLowerCase().startsWith(pfxHttp)) { - return { - innerProto: "http", - rest: s.substring(pfxHttp.length), - }; - } else { - return undefined; - } -} - -/** - * Parse a taler[+http]://pay URI. - * Return undefined if not passed a valid URI. - */ -export function parsePayUri(s: string): PayUriResult | undefined { - const pi = parseProtoInfo(s, "pay"); - if (!pi) { - return undefined; - } - const c = pi?.rest.split("?"); - const q = new URLSearchParams(c[1] ?? ""); - const claimToken = q.get("c") ?? undefined; - const parts = c[0].split("/"); - if (parts.length < 3) { - return undefined; - } - const host = parts[0].toLowerCase(); - const sessionId = parts[parts.length - 1]; - const orderId = parts[parts.length - 2]; - const pathSegments = parts.slice(1, parts.length - 2); - const p = [host, ...pathSegments].join("/"); - const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); - - return { - merchantBaseUrl, - orderId, - sessionId: sessionId, - claimToken, - }; -} - -/** - * Parse a taler[+http]://tip URI. - * Return undefined if not passed a valid URI. - */ -export function parseTipUri(s: string): TipUriResult | undefined { - const pi = parseProtoInfo(s, "tip"); - if (!pi) { - return undefined; - } - const c = pi?.rest.split("?"); - const parts = c[0].split("/"); - if (parts.length < 2) { - return undefined; - } - const host = parts[0].toLowerCase(); - const tipId = parts[parts.length - 1]; - const pathSegments = parts.slice(1, parts.length - 1); - const p = [host, ...pathSegments].join("/"); - const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); - - return { - merchantBaseUrl, - merchantTipId: tipId, - }; -} - -/** - * Parse a taler[+http]://refund URI. - * Return undefined if not passed a valid URI. - */ -export function parseRefundUri(s: string): RefundUriResult | undefined { - const pi = parseProtoInfo(s, "refund"); - if (!pi) { - return undefined; - } - const c = pi?.rest.split("?"); - const parts = c[0].split("/"); - if (parts.length < 3) { - return undefined; - } - const host = parts[0].toLowerCase(); - const sessionId = parts[parts.length - 1]; - const orderId = parts[parts.length - 2]; - const pathSegments = parts.slice(1, parts.length - 2); - const p = [host, ...pathSegments].join("/"); - const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`); - - return { - merchantBaseUrl, - orderId, - }; -} diff --git a/packages/taler-wallet-core/src/util/testvectors.ts b/packages/taler-wallet-core/src/util/testvectors.ts deleted file mode 100644 index 57ac6e992..000000000 --- a/packages/taler-wallet-core/src/util/testvectors.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - 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/> - */ - -/** - * Imports - */ -import { - setupRefreshPlanchet, - encodeCrock, - getRandomBytes, -} from "../crypto/talerCrypto"; - -export function printTestVectors() { - const secretSeed = getRandomBytes(64); - const coinIndex = Math.ceil(Math.random() * 100); - const p = setupRefreshPlanchet(secretSeed, coinIndex); - console.log("setupRefreshPlanchet"); - console.log(` (in) secret seed: ${encodeCrock(secretSeed)}`); - console.log(` (in) coin index: ${coinIndex}`); - console.log(` (out) blinding secret: ${encodeCrock(p.bks)}`); - console.log(` (out) coin priv: ${encodeCrock(p.coinPriv)}`); - console.log(` (out) coin pub: ${encodeCrock(p.coinPub)}`); -} diff --git a/packages/taler-wallet-core/src/util/time.ts b/packages/taler-wallet-core/src/util/time.ts deleted file mode 100644 index be63aef6b..000000000 --- a/packages/taler-wallet-core/src/util/time.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { Codec, renderContext, Context } from "./codec"; - -/* - This file is part of GNU Taler - (C) 2017-2019 Taler Systems S.A. - - 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/> - */ - -/** - * Helpers for relative and absolute time. - */ - -export class Timestamp { - /** - * Timestamp in milliseconds. - */ - readonly t_ms: number | "never"; -} - -export interface Duration { - /** - * Duration in milliseconds. - */ - readonly d_ms: number | "forever"; -} - -let timeshift = 0; - -export function setDangerousTimetravel(dt: number): void { - timeshift = dt; -} - -export function getTimestampNow(): Timestamp { - return { - t_ms: new Date().getTime() + timeshift, - }; -} - -export function isTimestampExpired(t: Timestamp) { - return timestampCmp(t, getTimestampNow()) <= 0; -} - -export function getDurationRemaining( - deadline: Timestamp, - now = getTimestampNow(), -): Duration { - if (deadline.t_ms === "never") { - return { d_ms: "forever" }; - } - if (now.t_ms === "never") { - throw Error("invalid argument for 'now'"); - } - if (deadline.t_ms < now.t_ms) { - return { d_ms: 0 }; - } - return { d_ms: deadline.t_ms - now.t_ms }; -} - -export function timestampMin(t1: Timestamp, t2: Timestamp): Timestamp { - if (t1.t_ms === "never") { - return { t_ms: t2.t_ms }; - } - if (t2.t_ms === "never") { - return { t_ms: t2.t_ms }; - } - return { t_ms: Math.min(t1.t_ms, t2.t_ms) }; -} - -export function timestampMax(t1: Timestamp, t2: Timestamp): Timestamp { - if (t1.t_ms === "never") { - return { t_ms: "never" }; - } - if (t2.t_ms === "never") { - return { t_ms: "never" }; - } - return { t_ms: Math.max(t1.t_ms, t2.t_ms) }; -} - -const SECONDS = 1000; -const MINUTES = SECONDS * 60; -const HOURS = MINUTES * 60; -const DAYS = HOURS * 24; -const MONTHS = DAYS * 30; -const YEARS = DAYS * 365; - -export function durationFromSpec(spec: { - seconds?: number; - minutes?: number; - hours?: number; - days?: number; - months?: number; - years?: number; -}): Duration { - let d_ms = 0; - d_ms += (spec.seconds ?? 0) * SECONDS; - d_ms += (spec.minutes ?? 0) * MINUTES; - d_ms += (spec.hours ?? 0) * HOURS; - d_ms += (spec.days ?? 0) * DAYS; - d_ms += (spec.months ?? 0) * MONTHS; - d_ms += (spec.years ?? 0) * YEARS; - return { d_ms }; -} - -/** - * Truncate a timestamp so that that it represents a multiple - * of seconds. The timestamp is always rounded down. - */ -export function timestampTruncateToSecond(t1: Timestamp): Timestamp { - if (t1.t_ms === "never") { - return { t_ms: "never" }; - } - return { - t_ms: Math.floor(t1.t_ms / 1000) * 1000, - }; -} - -export function durationMin(d1: Duration, d2: Duration): Duration { - if (d1.d_ms === "forever") { - return { d_ms: d2.d_ms }; - } - if (d2.d_ms === "forever") { - return { d_ms: d2.d_ms }; - } - return { d_ms: Math.min(d1.d_ms, d2.d_ms) }; -} - -export function durationMax(d1: Duration, d2: Duration): Duration { - if (d1.d_ms === "forever") { - return { d_ms: "forever" }; - } - if (d2.d_ms === "forever") { - return { d_ms: "forever" }; - } - return { d_ms: Math.max(d1.d_ms, d2.d_ms) }; -} - -export function durationMul(d: Duration, n: number): Duration { - if (d.d_ms === "forever") { - return { d_ms: "forever" }; - } - return { d_ms: Math.round(d.d_ms * n) }; -} - -export function durationAdd(d1: Duration, d2: Duration): Duration { - if (d1.d_ms === "forever" || d2.d_ms === "forever") { - return { d_ms: "forever" }; - } - return { d_ms: d1.d_ms + d2.d_ms }; -} - -export function timestampCmp(t1: Timestamp, t2: Timestamp): number { - if (t1.t_ms === "never") { - if (t2.t_ms === "never") { - return 0; - } - return 1; - } - if (t2.t_ms === "never") { - return -1; - } - if (t1.t_ms == t2.t_ms) { - return 0; - } - if (t1.t_ms > t2.t_ms) { - return 1; - } - return -1; -} - -export function timestampAddDuration(t1: Timestamp, d: Duration): Timestamp { - if (t1.t_ms === "never" || d.d_ms === "forever") { - return { t_ms: "never" }; - } - return { t_ms: t1.t_ms + d.d_ms }; -} - -export function timestampSubtractDuraction( - t1: Timestamp, - d: Duration, -): Timestamp { - if (t1.t_ms === "never") { - return { t_ms: "never" }; - } - if (d.d_ms === "forever") { - return { t_ms: 0 }; - } - return { t_ms: Math.max(0, t1.t_ms - d.d_ms) }; -} - -export function stringifyTimestamp(t: Timestamp): string { - if (t.t_ms === "never") { - return "never"; - } - return new Date(t.t_ms).toISOString(); -} - -export function timestampDifference(t1: Timestamp, t2: Timestamp): Duration { - if (t1.t_ms === "never") { - return { d_ms: "forever" }; - } - if (t2.t_ms === "never") { - return { d_ms: "forever" }; - } - return { d_ms: Math.abs(t1.t_ms - t2.t_ms) }; -} - -export function timestampIsBetween( - t: Timestamp, - start: Timestamp, - end: Timestamp, -): boolean { - if (timestampCmp(t, start) < 0) { - return false; - } - if (timestampCmp(t, end) > 0) { - return false; - } - return true; -} - -export const codecForTimestamp: Codec<Timestamp> = { - decode(x: any, c?: Context): Timestamp { - const t_ms = x.t_ms; - if (typeof t_ms === "string") { - if (t_ms === "never") { - return { t_ms: "never" }; - } - throw Error(`expected timestamp at ${renderContext(c)}`); - } - if (typeof t_ms === "number") { - return { t_ms }; - } - throw Error(`expected timestamp at ${renderContext(c)}`); - }, -}; - -export const codecForDuration: Codec<Duration> = { - decode(x: any, c?: Context): Duration { - const d_ms = x.d_ms; - if (typeof d_ms === "string") { - if (d_ms === "forever") { - return { d_ms: "forever" }; - } - throw Error(`expected duration at ${renderContext(c)}`); - } - if (typeof d_ms === "number") { - return { d_ms }; - } - throw Error(`expected duration at ${renderContext(c)}`); - }, -}; diff --git a/packages/taler-wallet-core/src/util/timer.ts b/packages/taler-wallet-core/src/util/timer.ts index 75fb5c9c0..9133bd572 100644 --- a/packages/taler-wallet-core/src/util/timer.ts +++ b/packages/taler-wallet-core/src/util/timer.ts @@ -24,7 +24,7 @@ /** * Imports. */ -import { Duration } from "./time"; +import { Duration } from "@gnu-taler/taler-util"; import { Logger } from "./logging"; const logger = new Logger("timer.ts"); diff --git a/packages/taler-wallet-core/src/util/wire.ts b/packages/taler-wallet-core/src/util/wire.ts deleted file mode 100644 index 95e324f3c..000000000 --- a/packages/taler-wallet-core/src/util/wire.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - This file is part of TALER - (C) 2017 GNUnet e.V. - - 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. - - 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Display and manipulate wire information. - * - * Right now, all types are hard-coded. In the future, there might be plugins / configurable - * methods or support for the "payto://" URI scheme. - */ - -/** - * Imports. - */ -import * as i18n from "../i18n"; - -/** - * Short summary of the wire information. - * - * Might abbreviate and return the same summary for different - * wire details. - */ -export function summarizeWire(w: any): string { - if (!w.type) { - return i18n.str`Invalid Wire`; - } - switch (w.type.toLowerCase()) { - case "test": - if (!w.account_number && w.account_number !== 0) { - return i18n.str`Invalid Test Wire Detail`; - } - if (!w.bank_uri) { - return i18n.str`Invalid Test Wire Detail`; - } - return i18n.str`Test Wire Acct #${w.account_number} on ${w.bank_uri}`; - default: - return i18n.str`Unknown Wire Detail`; - } -} |