diff --git a/package.json b/package.json index 7560d40941..9d613091b6 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@babel/core": "^7.2.0", - "@polkadot/dev": "^0.22.11", + "@polkadot/dev": "^0.22.12", "@polkadot/ts": "^0.1.43" } } diff --git a/packages/types/src/Digest.ts b/packages/types/src/Digest.ts index 61a58a2d1c..b764dd6c59 100644 --- a/packages/types/src/Digest.ts +++ b/packages/types/src/Digest.ts @@ -72,12 +72,12 @@ export class Seal extends Tuple { export class DigestItem extends EnumType { constructor (value: any) { - super([ + super({ Other, // Position 0, as per Rust (encoding control) AuthoritiesChange, ChangesTrieRoot, Seal - ], value); + }, value); } } diff --git a/packages/types/src/EventRecord.ts b/packages/types/src/EventRecord.ts index 93ab1725d5..aca46c05a3 100644 --- a/packages/types/src/EventRecord.ts +++ b/packages/types/src/EventRecord.ts @@ -31,10 +31,10 @@ export class Finalization extends Null { */ export class Phase extends EnumType { constructor (value: any, index?: number) { - super([ + super({ ApplyExtrinsic, Finalization - ], value, index); + }, value, index); } } diff --git a/packages/types/src/ExtrinsicStatus.ts b/packages/types/src/ExtrinsicStatus.ts index 8e47dd3ace..3b7d63a131 100644 --- a/packages/types/src/ExtrinsicStatus.ts +++ b/packages/types/src/ExtrinsicStatus.ts @@ -47,11 +47,11 @@ export class Usurped extends Hash { */ export default class ExtrinsicStatus extends EnumType { constructor (value: any, index?: number) { - super([ + super({ Finalised, Usurped, Broadcast, Dropped - ], value, index); + }, value, index); } } diff --git a/packages/types/src/Metadata/Modules.ts b/packages/types/src/Metadata/Modules.ts index 09617719f5..53c89dd1ad 100644 --- a/packages/types/src/Metadata/Modules.ts +++ b/packages/types/src/Metadata/Modules.ts @@ -152,10 +152,10 @@ export class StorageFunctionType$Map extends Struct { export class StorageFunctionType extends EnumType { constructor (value?: any, index?: number) { - super([ + super({ Type, StorageFunctionType$Map - ], value, index); + }, value, index); } /** diff --git a/packages/types/src/MisbehaviorReport.ts b/packages/types/src/MisbehaviorReport.ts index 2db77b788f..34c5b62600 100644 --- a/packages/types/src/MisbehaviorReport.ts +++ b/packages/types/src/MisbehaviorReport.ts @@ -120,12 +120,12 @@ export class BftDoubleCommit extends BftAtReport { */ export class MisbehaviorKind extends EnumType { constructor (value?: BftAtReportValue | Uint8Array, index?: number) { - super([ + super({ BftProposeOutOfTurn, BftDoublePropose, BftDoublePrepare, BftDoubleCommit - ], value, index); + }, value, index); } } diff --git a/packages/types/src/codec/EnumType.spec.js b/packages/types/src/codec/EnumType.spec.ts similarity index 53% rename from packages/types/src/codec/EnumType.spec.js rename to packages/types/src/codec/EnumType.spec.ts index 99db405465..f9d7d96f3f 100644 --- a/packages/types/src/codec/EnumType.spec.js +++ b/packages/types/src/codec/EnumType.spec.ts @@ -7,25 +7,46 @@ import Text from '../Text'; import U32 from '../U32'; describe('Struct', () => { - it('provides a clean toString() (raw)', () => { + it('provides a clean toString() (value)', () => { expect( new EnumType( - [Text, U32], + { Text, U32 }, new Uint8Array([0, 2 << 2, 49, 50]) - ).raw.toString() + ).value.toString() ).toEqual('12'); }); it('provides a clean toString() (enum)', () => { expect( new EnumType( - [Text, U32], + { Text, U32 }, new Uint8Array([1, 2 << 2, 49, 50]) ).toString() ).toEqual('U32'); }); - it('allows checking against defined indexes', () => { + it('decodes from a JSON input (lowercase)', () => { + expect( + new EnumType( + { Text, U32 }, + { 'text': 'some text value' } + ).value.toString() + ).toEqual('some text value'); + }); + + it('decodes from a JSON input (mixed case)', () => { + expect( + new EnumType( + { Text, U32 }, + { 'U32': 42 } + ).value.toString() + ).toEqual('42'); + }); + + // We are currently not using this approach, none of the types in Substrate currently + // have any overrides. Insteda of trying to support it (just-in-case), rather have it + // removed to simplyfy the code - it can be pulled-back if needed + it.skip('allows checking against defined indexes', () => { expect( new EnumType( { 1: Text, 5: U32 }, @@ -37,7 +58,7 @@ describe('Struct', () => { it('allows accessing the type and value', () => { const text = new Text('foo'); const enumType = new EnumType( - [Text, U32], + { Text, U32 }, { Text: text } ); diff --git a/packages/types/src/codec/EnumType.ts b/packages/types/src/codec/EnumType.ts index 01100f8cf2..fd0690aecc 100644 --- a/packages/types/src/codec/EnumType.ts +++ b/packages/types/src/codec/EnumType.ts @@ -2,16 +2,20 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { isNumber, isObject, isU8a, isUndefined, u8aConcat, u8aToHex } from '@polkadot/util'; +import { assert, isNumber, isObject, isU8a, u8aConcat, u8aToHex } from '@polkadot/util'; import Base from './Base'; import Null from '../Null'; import { Codec, Constructor } from '../types'; -type TypesArray = Array; type TypesDef = { - [index: number]: Constructor -} | TypesArray; + [name: string]: Constructor +}; + +type Decoded = { + index: number, + value: Codec +}; /** * @name EnumType @@ -23,62 +27,61 @@ type TypesDef = { // - As per Enum, actually use TS enum // - It should rather probably extend Enum instead of copying code export default class EnumType extends Base implements Codec { - private _Types: TypesArray; + private _def: TypesDef; private _index: number; private _indexes: Array; constructor (def: TypesDef, value?: any, index?: number | EnumType) { const decoded = EnumType.decodeEnumType(def, value, index); - super( - decoded.value - ); - - this._Types = Array.isArray(def) - ? def - : Object.values(def); - this._indexes = Object.keys(def).map((index) => - parseInt(index, 10) - ); + super(decoded.value); + this._def = def; + this._indexes = Object.keys(def).map((_, index) => index); this._index = this._indexes.indexOf(decoded.index) || 0; } - static decodeEnumType (def: TypesDef, value?: any, index?: number | EnumType): { index: number, value: any } { + private static decodeEnumType (def: TypesDef, value?: any, index?: number | EnumType): Decoded { // If `index` is set, we parse it. if (index instanceof EnumType) { - return { index: index._index, value: new def[index._index](index.raw) }; + return EnumType.createValue(def, index._index, index.raw); } else if (isNumber(index)) { - return { index, value: new def[index](value) }; + return EnumType.createValue(def, index, value); } // Or else, we just look at `value` - if (isU8a(value)) { - return { index: value[0], value: new def[value[0]](value.subarray(1)) }; - } else if (isNumber(value) && !isUndefined(def[value])) { - return { index: value, value: new def[value]() }; + return EnumType.decodeViaValue(def, value); + } + + private static decodeViaValue (def: TypesDef, value?: any): Decoded { + if (value instanceof EnumType) { + return EnumType.createValue(def, value._index, value.raw); + } else if (isU8a(value)) { + return EnumType.createValue(def, value[0], value.subarray(1)); + } else if (isNumber(value)) { + return EnumType.createValue(def, value); } else if (isObject(value)) { // JSON comes in the form of { "": "" }, here we // additionally force to lower to ensure forward compat const key = Object.keys(value)[0]; const lowerKey = key.toLowerCase(); - const index = Object - .keys(def) - .find((k) => def[+k].name.toLowerCase() === lowerKey); + const keys = Object.keys(def).map((k) => k.toLowerCase()); + const index = keys.indexOf(lowerKey); - if (isUndefined(index)) { - const message = 'Unable to reliably map input on JSON'; + assert(index !== -1, 'Unable to reliably map input on JSON'); - console.error(message, value, def); - - throw new Error(message); - } - - return { index: +index, value: new def[+index](value[key]) }; + return EnumType.createValue(def, index, value[key]); } - // Worst-case scenario, return this - return { index: 0, value: new (Object.values(def)[0])() }; + // Worst-case scenario, return the first with default + return EnumType.createValue(def, 0); + } + + private static createValue (def: TypesDef, index: number, value?: any): Decoded { + return { + index, + value: new (Object.values(def)[index])(value) + }; } /** @@ -106,7 +109,7 @@ export default class EnumType extends Base implements Codec { * @description The name of the type this enum value represents */ get type (): string { - return this._Types[this._index].name; + return Object.keys(this._def)[this._index]; } /** diff --git a/yarn.lock b/yarn.lock index 557f6f7738..401b94b3de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1327,10 +1327,10 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26" integrity sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw== -"@polkadot/dev@^0.22.11": - version "0.22.11" - resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.22.11.tgz#fcacaba21cb7f72490ad67e228171ce6cf5903f7" - integrity sha512-xYyAknjwqNZqDpxNgbH137R5m3jACQjnJi7/rz3uR8GFW107rFb7sGTtvtX3TYaS7CP69Eiy+mCRCE+3bdKHsw== +"@polkadot/dev@^0.22.12": + version "0.22.12" + resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.22.12.tgz#f0eaf211efb3d6b70869dab63a022858a110de85" + integrity sha512-dF4LJHrDOLbn7AqBm2ZL8o9YqUsm0+00OED8XCI0n2jy7jvMd37PwPn3gib0Ip7xwSZiikJbHvNkvmyGUUZjpw== dependencies: "@babel/cli" "^7.2.0" "@babel/core" "^7.2.0"