From f381a4d045fbb8aa9229a7e7cdb5dee482c45461 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 15 Nov 2018 14:19:41 +0100 Subject: [PATCH] Text extends String (#399) * Text extends String * Fix type * Cleaner code * Add explicit return type --- packages/types/src/Text.spec.ts | 5 ++- packages/types/src/Text.ts | 30 ++++++------- packages/types/src/Type.ts | 76 ++++++++++++++++++--------------- 3 files changed, 57 insertions(+), 54 deletions(-) diff --git a/packages/types/src/Text.spec.ts b/packages/types/src/Text.spec.ts index d261ae083d..3b178628e0 100644 --- a/packages/types/src/Text.spec.ts +++ b/packages/types/src/Text.spec.ts @@ -2,6 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. +import { CodecTo } from './types'; import Text from './Text'; import U8a from './codec/U8a'; @@ -20,12 +21,12 @@ describe('Text', () => { }); describe('encode', () => { - const testEncode = (to: 'toString' | 'toU8a', expected: string | Uint8Array) => + const testEncode = (to: CodecTo, expected: string | Uint8Array) => it(`can encode ${to}`, () => { expect(new Text('foo')[to]()).toEqual(expected); }); - // testEncode('toHex', '0x0c666f6f'); // FIXME Add this + testEncode('toHex', '0x0c666f6f'); testEncode('toString', 'foo'); testEncode('toU8a', Uint8Array.from([12, 102, 111, 111])); }); diff --git a/packages/types/src/Text.ts b/packages/types/src/Text.ts index 72ac20cb59..447ad700c5 100644 --- a/packages/types/src/Text.ts +++ b/packages/types/src/Text.ts @@ -2,10 +2,9 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import { isFunction, isString, stringToU8a, u8aToString, u8aConcat } from '@polkadot/util'; +import { isFunction, isString, stringToU8a, u8aToString, u8aConcat, u8aToHex } from '@polkadot/util'; -import { AnyU8a } from '@polkadot/types/types'; -import Base from './codec/Base'; +import { AnyU8a, Codec } from './types'; import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact'; // This is a string wrapper, along with the length. It is used both for strings as well @@ -16,18 +15,19 @@ import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact'; // - Potentially we want a "TypeString" extension to this. Basically something that // wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString" // we can nicely strip types down like "T::AcountId" -> "AccountId" -export default class Text extends Base { +export default class Text extends String implements Codec { + public raw: String; // FIXME Remove this once we convert all types out of Base constructor (value: Text | string | AnyU8a | { toString: () => string } = '') { super( Text.decodeText(value) ); + + this.raw = this; } - static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string { + private static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string { if (isString(value)) { - return value; - } else if (value instanceof Text) { - return value.raw; + return value.toString(); } else if (value instanceof Uint8Array) { const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS); @@ -39,24 +39,20 @@ export default class Text extends Base { return `${value}`; } - get length (): number { - return this.raw.length; - } - get encodedLength (): number { return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length; } + toHex (): string { + return u8aToHex(this.toU8a()); + } + toJSON (): any { return this.toString(); } - toString (): string { - return this.raw; - } - toU8a (isBare?: boolean): Uint8Array { - const encoded = stringToU8a(this.raw); + const encoded = stringToU8a(this.toString()); return isBare ? encoded diff --git a/packages/types/src/Type.ts b/packages/types/src/Type.ts index a91b8a27f5..e57380282a 100644 --- a/packages/types/src/Type.ts +++ b/packages/types/src/Type.ts @@ -2,6 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. +import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact'; import U8a from './codec/U8a'; import Text from './Text'; @@ -13,30 +14,24 @@ const ALLOWED_BOXES = ['Compact', 'Option', 'Vec']; // what string provides us, however we also "tweak" the types received from the runtime, i.e. // we remove the `T::` prefixes found in some types for consistency accross implementation. export default class Type extends Text { - private _originalLength: number = 0; + private _originalLength: number; - constructor (value?: Text | U8a | Uint8Array | string) { - super(value); + constructor (value: Text | U8a | Uint8Array | string = '') { + // First decode it with Text + const textValue = new Text(value).toString(); + // Then cleanup the textValue to get the @polkadot/types type, and pass the + // sanitized value to constructor + super( + Type.decodeType( + textValue + ) + ); + + this._originalLength = textValue.length; - this._cleanupTypes(); } - // NOTE Length is used in the decoding calculations, so return the original (pre-cleanup) - // length of the data. Since toU8a is disabled, this does not affect encoding, but rather - // only the decoding leg, allowing the decoders to work with original pointers - get length (): number { - return this._originalLength; - } - - // Note Since we are mangling what we get in beyond recognition, we really should - // not allow the re-encoding. Additionally, this is probably more of a decoder-only - // helper, so treat it as such. - toU8a (isBare?: boolean): Uint8Array { - throw new Error('Type::toU8a: unimplemented'); - } - - // HACK(ery) Take the types and tweak them (slightly?) for consistency - private _cleanupTypes (): Type { + private static decodeType (value: string): string { const mappings: Array = [ // this._cleanupCompact(), @@ -58,16 +53,27 @@ export default class Type extends Text { // `PropIndex` -> `ProposalIndex` (implementation looks the same, however meant as diff) ]; - this._originalLength = this.raw.length; - this.raw = mappings.reduce((result, fn) => { + return mappings.reduce((result, fn) => { return fn(result); - }, this.raw).trim(); + }, value).trim(); + } - return this; + // NOTE Length is used in the decoding calculations, so return the original (pre-cleanup) + // length of the data. Since toU8a is disabled, this does not affect encoding, but rather + // only the decoding leg, allowing the decoders to work with original pointers + get encodedLength (): number { + return this._originalLength + Compact.encodeU8a(this._originalLength, DEFAULT_LENGTH_BITS).length; + } + + // Note Since we are mangling what we get in beyond recognition, we really should + // not allow the re-encoding. Additionally, this is probably more of a decoder-only + // helper, so treat it as such. + toU8a (isBare?: boolean): Uint8Array { + throw new Error('Type::toU8a: unimplemented'); } // given a starting index, find the closing > - private _findClosing (value: string, start: number): number { + private static _findClosing (value: string, start: number): number { let depth = 0; for (let index = start; index < value.length; index++) { @@ -85,7 +91,7 @@ export default class Type extends Text { throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`); } - private _alias (src: string, dest: string): Mapper { + private static _alias (src: string, dest: string): Mapper { return (value: string): string => { return value.replace( new RegExp(src, 'g'), dest @@ -93,14 +99,14 @@ export default class Type extends Text { }; } - private _cleanupCompact (): Mapper { + private static _cleanupCompact (): Mapper { return (value: string): string => { for (let index = 0; index < value.length; index++) { if (value[index] !== '<') { continue; } - const end = this._findClosing(value, index + 1) - 14; + const end = Type._findClosing(value, index + 1) - 14; if (value.substr(end, 14) === ' as HasCompact') { value = `Compact<${value.substr(index + 1, end - index - 1)}>`; @@ -111,7 +117,7 @@ export default class Type extends Text { }; } - private _removeGenerics (): Mapper { + private static _removeGenerics (): Mapper { return (value: string): string => { for (let index = 0; index < value.length; index++) { if (value[index] === '<') { @@ -124,7 +130,7 @@ export default class Type extends Text { // we have not found anything, unwrap generic innards if (!box) { - const end = this._findClosing(value, index + 1); + const end = Type._findClosing(value, index + 1); value = `${value.substr(0, index)}${value.substr(end + 1)}`; } @@ -136,12 +142,12 @@ export default class Type extends Text { } // remove the PairOf wrappers - private _removePairOf (): Mapper { + private static _removePairOf (): Mapper { return (value: string): string => { for (let index = 0; index < value.length; index++) { if (value.substr(index, 7) === 'PairOf<') { const start = index + 7; - const end = this._findClosing(value, start); + const end = Type._findClosing(value, start); const type = value.substr(start, end - start); value = `${value.substr(0, index)}(${type}, ${type})${value.substr(end + 1)}`; @@ -153,7 +159,7 @@ export default class Type extends Text { } // remove the type traits - private _removeTraits (): Mapper { + private static _removeTraits (): Mapper { return (value: string): string => { return value // anything `T::` to end up as `` @@ -168,7 +174,7 @@ export default class Type extends Text { } // remove wrapping values, i.e. Box -> Proposal - private _removeWrap (_check: string): Mapper { + private static _removeWrap (_check: string): Mapper { const check = `${_check}<`; return (value: string): string => { @@ -179,7 +185,7 @@ export default class Type extends Text { if (index !== -1) { const start = index + check.length; - const end = this._findClosing(value, start); + const end = Type._findClosing(value, start); value = `${value.substr(0, index)}${value.substr(start, end - start)}${value.substr(end + 1)}`; }