Combine Enum & EnumType (#922)
* Extend EnumType with basic (convert use) * Remove Enum, adjust EnumType tests with Enum version * Remove aliasses * test json finaised -> finalized
This commit is contained in:
@@ -14,7 +14,6 @@ These are the base types of the codec. They are typically not used directly, but
|
||||
| [[Base]] | A type extends the Base class, when it holds a value |
|
||||
| [[Compact]] | A compact length-encoding codec wrapper. Mostly used by other types to add length-prefixed encoding |
|
||||
| [[Enum]] | A codec wrapper for an enum. Enums are encoded as a single byte, where the byte is a zero-indexed value |
|
||||
| [[EnumType]] | Implements an enum, which wraps a different type based on the value|
|
||||
| [[Int]] | A generic signed integer codec |
|
||||
| [[Option]] | An Option is an optional field. The first byte indicates that there is is value to follow |
|
||||
| [[Set]] | An Set is an array of string values, represented an an encoded type by a bitwise representation of the values |
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
import { assert, isUndefined } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Null from '../primitive/Null';
|
||||
import MagicNumber from './MagicNumber';
|
||||
import MetadataV0 from './v0';
|
||||
import MetadataV1 from './v1';
|
||||
@@ -18,10 +17,7 @@ import v1ToV2 from './v1/toV2';
|
||||
import v2ToV3 from './v2/toV3';
|
||||
import v3ToV4 from './v3/toV4';
|
||||
|
||||
class MetadataDeprecated extends Null {
|
||||
}
|
||||
|
||||
class MetadataEnum extends EnumType<MetadataDeprecated | MetadataV0 | MetadataV1 | MetadataV2 | MetadataV3 | MetadataV4> {
|
||||
class MetadataEnum extends Enum {
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
MetadataV0, // once rolled-out, can replace this with MetadataDeprecated
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AnyNumber } from '../../types';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Enum from '../../codec/Enum';
|
||||
import EnumType from '../../codec/EnumType';
|
||||
import Struct from '../../codec/Struct';
|
||||
import Vector from '../../codec/Vector';
|
||||
import Bytes from '../../primitive/Bytes';
|
||||
@@ -71,7 +70,7 @@ export class MapType extends Struct {
|
||||
export class PlainType extends Type {
|
||||
}
|
||||
|
||||
export class StorageFunctionType extends EnumType<PlainType | MapType> {
|
||||
export class StorageFunctionType extends Enum {
|
||||
constructor (value?: any, index?: number) {
|
||||
super({
|
||||
PlainType,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AnyNumber } from '../../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../../codec/EnumType';
|
||||
import Enum from '../../codec/Enum';
|
||||
import Struct from '../../codec/Struct';
|
||||
import Vector from '../../codec/Vector';
|
||||
import Bool from '../../primitive/Bool';
|
||||
@@ -52,7 +52,7 @@ export class MapType extends Struct {
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageFunctionType extends EnumType<PlainType | MapType> {
|
||||
export class StorageFunctionType extends Enum {
|
||||
constructor (value?: any, index?: number) {
|
||||
super({
|
||||
PlainType,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AnyNumber } from '../../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../../codec/EnumType';
|
||||
import Enum from '../../codec/Enum';
|
||||
import Struct from '../../codec/Struct';
|
||||
import Vector from '../../codec/Vector';
|
||||
import Bytes from '../../primitive/Bytes';
|
||||
@@ -64,7 +64,7 @@ export class DoubleMapType extends Struct {
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageFunctionType extends EnumType<PlainType | MapType | DoubleMapType> {
|
||||
export class StorageFunctionType extends Enum {
|
||||
constructor (value?: any, index?: number) {
|
||||
super({
|
||||
PlainType,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AnyNumber } from '../../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../../codec/EnumType';
|
||||
import Enum from '../../codec/Enum';
|
||||
import Struct from '../../codec/Struct';
|
||||
import Vector from '../../codec/Vector';
|
||||
import Bool from '../../primitive/Bool';
|
||||
@@ -108,7 +108,7 @@ export class DoubleMapType extends Struct {
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageFunctionType extends EnumType<PlainType | MapType | DoubleMapType> {
|
||||
export class StorageFunctionType extends Enum {
|
||||
constructor (value?: any, index?: number) {
|
||||
super({
|
||||
PlainType,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Enum from './Enum';
|
||||
|
||||
const testDecode = (type: string, input: any, expected: any) =>
|
||||
it(`can decode from ${type}`, () => {
|
||||
const e = new Enum(['foo', 'bar'], input);
|
||||
expect(e.toString()).toBe(expected);
|
||||
});
|
||||
|
||||
const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any) =>
|
||||
it(`can encode ${to}`, () => {
|
||||
const e = new Enum(['foo', 'bar'], 1);
|
||||
|
||||
expect(e[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
describe('Enum', () => {
|
||||
testDecode('Enum', undefined, 'foo');
|
||||
testDecode('Enum', new Enum([], 1), 'bar');
|
||||
testDecode('number', 0, 'foo');
|
||||
testDecode('number', 1, 'bar');
|
||||
testDecode('string', 'bar', 'bar');
|
||||
testDecode('Uint8Array', Uint8Array.from([0]), 'foo');
|
||||
testDecode('Uint8Array', Uint8Array.from([1]), 'bar');
|
||||
|
||||
testEncode('toJSON', 1);
|
||||
testEncode('toNumber', 1);
|
||||
testEncode('toString', 'bar');
|
||||
testEncode('toU8a', Uint8Array.from([1]));
|
||||
|
||||
it('provides a clean toString()', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar']).toString()
|
||||
).toEqual('foo');
|
||||
});
|
||||
|
||||
it('provides a clean toString() (enum)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], new Enum([], 1)).toNumber()
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it('converts to and from U8a', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], new Uint8Array([1])).toU8a()
|
||||
).toEqual(new Uint8Array([1]));
|
||||
});
|
||||
|
||||
it('converts from JSON', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 5).toString()
|
||||
).toEqual('5');
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
it('compares agains the index value', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq(1)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('compares agains the index value (false)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq(0)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('compares agains the string value', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq('bar')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('compares agains the string value (false)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq('foo')
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,129 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { isString, isU8a, u8aToHex, assert } from '@polkadot/util';
|
||||
import Enum from './EnumType';
|
||||
|
||||
import { Codec } from '../types';
|
||||
import Base from './Base';
|
||||
|
||||
type EnumMap = {
|
||||
[index: number]: string
|
||||
};
|
||||
|
||||
type EnumDef = {
|
||||
[index: string]: number
|
||||
} | Array<string>;
|
||||
|
||||
/**
|
||||
* @name Enum
|
||||
* @description
|
||||
* A codec wrapper for an enum. Enums are encoded as a single byte, where the byte
|
||||
* is a zero-indexed value. This class allows you to retrieve the value either
|
||||
* by `toNumber()` exposing the actual raw index, or `toString()` returning a
|
||||
* string representation (as provided as part of the constructor)
|
||||
*/
|
||||
// TODO:
|
||||
// - It would be great if this could actually wrap actual TS enums
|
||||
export default class Enum extends Base<number> implements Codec {
|
||||
private _enum: EnumMap | Array<string>;
|
||||
|
||||
constructor (def: EnumDef, value: Enum | Uint8Array | string | number = 0) {
|
||||
const decoded = Enum.decodeEnum(def, value);
|
||||
|
||||
assert(decoded !== -1, `Unable to initialise Enum with value ${value}`);
|
||||
|
||||
super(decoded);
|
||||
|
||||
this._enum = Array.isArray(def)
|
||||
? def
|
||||
: Object.keys(def).reduce((result, key) => {
|
||||
result[def[key]] = key;
|
||||
|
||||
return result;
|
||||
}, {} as EnumMap);
|
||||
}
|
||||
|
||||
static decodeEnum (def: EnumDef, value: Enum | Uint8Array | string | number): number | undefined {
|
||||
if (value instanceof Enum) {
|
||||
return value.raw;
|
||||
} else if (isU8a(value)) {
|
||||
return value[0];
|
||||
} else if (isString(value)) {
|
||||
return Array.isArray(def)
|
||||
? def.indexOf(value)
|
||||
: def[value] || -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The length of the value when encoded as a Uint8Array
|
||||
*/
|
||||
get encodedLength (): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the index for this value (equivalent to toNumber)
|
||||
*/
|
||||
get index (): number {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the value is an empty value (always false)
|
||||
*/
|
||||
get isEmpty (): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Compares the value of the input to see if there is a match
|
||||
*/
|
||||
eq (other?: any): boolean {
|
||||
if (other instanceof Enum) {
|
||||
return this.raw === other.raw;
|
||||
}
|
||||
|
||||
return isString(other)
|
||||
? this.toString() === other
|
||||
: this.raw === other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a hex string representation of the value
|
||||
*/
|
||||
toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Converts the Object to JSON, typically used for RPC transfers
|
||||
*/
|
||||
toJSON (): any {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the number representation for the value
|
||||
*/
|
||||
toNumber (): number {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the string representation of the value
|
||||
*/
|
||||
toString (): string {
|
||||
return this._enum[this.raw] || `${this.raw}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Encodes the value as a Uint8Array as per the parity-codec specifications
|
||||
* @param isBare true when the value has none of the type-specific prefixes (internal)
|
||||
*/
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return new Uint8Array([this.raw]);
|
||||
}
|
||||
}
|
||||
export default Enum;
|
||||
|
||||
@@ -2,132 +2,213 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import EnumType from './EnumType';
|
||||
import Enum from './Enum';
|
||||
import Null from '../primitive/Null';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
|
||||
describe('Struct', () => {
|
||||
it('provides a clean toString() (value)', () => {
|
||||
expect(
|
||||
new EnumType(
|
||||
{ Text, U32 },
|
||||
new Uint8Array([0, 2 << 2, 49, 50])
|
||||
).value.toString()
|
||||
).toEqual('12');
|
||||
});
|
||||
|
||||
it('provides a clean toString() (enum)', () => {
|
||||
expect(
|
||||
new EnumType(
|
||||
{ Text, U32 },
|
||||
new Uint8Array([1, 2 << 2, 49, 50])
|
||||
).toString()
|
||||
).toEqual('{"U32":3289352}');
|
||||
});
|
||||
|
||||
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 hex', () => {
|
||||
expect(
|
||||
new EnumType(
|
||||
{ Text, U32 },
|
||||
'0x0134120000'
|
||||
).value.toString()
|
||||
).toEqual('4660'); // 0x1234 in decimal
|
||||
});
|
||||
|
||||
it('decodes from a JSON input (mixed case)', () => {
|
||||
expect(
|
||||
new EnumType(
|
||||
{ Text, U32 },
|
||||
{ 'U32': 42 }
|
||||
).value.toString()
|
||||
).toEqual('42');
|
||||
});
|
||||
|
||||
it('decodes from JSON string', () => {
|
||||
expect(
|
||||
new EnumType(
|
||||
{ Null, U32 },
|
||||
'null'
|
||||
).type
|
||||
).toEqual('Null');
|
||||
});
|
||||
|
||||
it('stringifies with custom types', () => {
|
||||
class A extends Null { }
|
||||
class B extends Null { }
|
||||
class C extends Null { }
|
||||
class Test extends EnumType<A | B | C> {
|
||||
constructor (value?: string, index?: number) {
|
||||
super({
|
||||
a: A,
|
||||
b: B,
|
||||
c: C
|
||||
}, value, index);
|
||||
}
|
||||
}
|
||||
|
||||
expect(new Test().toJSON()).toEqual({ a: null });
|
||||
});
|
||||
|
||||
it('creates via with', () => {
|
||||
class A extends Null { }
|
||||
class B extends U32 { }
|
||||
class C extends Null { }
|
||||
const Test = EnumType.with({ A, B, C });
|
||||
|
||||
expect(new Test().toJSON()).toEqual({ A: null });
|
||||
expect(new Test(1234, 1).toJSON()).toEqual({ B: 1234 });
|
||||
});
|
||||
|
||||
// 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 },
|
||||
new Uint8Array([1, 2 << 2, 49, 50])
|
||||
).toString()
|
||||
).toEqual('Text');
|
||||
});
|
||||
|
||||
it('allows accessing the type and value', () => {
|
||||
const text = new Text('foo');
|
||||
const enumType = new EnumType(
|
||||
{ Text, U32 },
|
||||
{ Text: text }
|
||||
);
|
||||
|
||||
expect(enumType.type).toBe('Text');
|
||||
expect(enumType.value).toEqual(text);
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
const eqtest = new EnumType(
|
||||
{ U32, Text },
|
||||
new Uint8Array([1, 3 << 2, 88, 89, 90])
|
||||
);
|
||||
|
||||
it('compares against index', () => {
|
||||
describe('Enum', () => {
|
||||
describe('typed enum (previously EnumType)', () => {
|
||||
it('provides a clean toString() (value)', () => {
|
||||
expect(
|
||||
eqtest.eq(1)
|
||||
).toBe(true);
|
||||
new Enum(
|
||||
{ Text, U32 },
|
||||
new Uint8Array([0, 2 << 2, 49, 50])
|
||||
).value.toString()
|
||||
).toEqual('12');
|
||||
});
|
||||
|
||||
it('compares against values', () => {
|
||||
it('provides a clean toString() (enum)', () => {
|
||||
expect(
|
||||
eqtest.eq('XYZ')
|
||||
).toBe(true);
|
||||
new Enum(
|
||||
{ Text, U32 },
|
||||
new Uint8Array([1, 2 << 2, 49, 50])
|
||||
).toString()
|
||||
).toEqual('{"U32":3289352}');
|
||||
});
|
||||
|
||||
it('decodes from a JSON input (lowercase)', () => {
|
||||
expect(
|
||||
new Enum(
|
||||
{ Text, U32 },
|
||||
{ 'text': 'some text value' }
|
||||
).value.toString()
|
||||
).toEqual('some text value');
|
||||
});
|
||||
|
||||
it('decodes from hex', () => {
|
||||
expect(
|
||||
new Enum(
|
||||
{ Text, U32 },
|
||||
'0x0134120000'
|
||||
).value.toString()
|
||||
).toEqual('4660'); // 0x1234 in decimal
|
||||
});
|
||||
|
||||
it('decodes from a JSON input (mixed case)', () => {
|
||||
expect(
|
||||
new Enum(
|
||||
{ Text, U32 },
|
||||
{ 'U32': 42 }
|
||||
).value.toString()
|
||||
).toEqual('42');
|
||||
});
|
||||
|
||||
it('decodes from JSON string', () => {
|
||||
expect(
|
||||
new Enum(
|
||||
{ Null, U32 },
|
||||
'null'
|
||||
).type
|
||||
).toEqual('Null');
|
||||
});
|
||||
|
||||
it('stringifies with custom types', () => {
|
||||
class A extends Null { }
|
||||
class B extends Null { }
|
||||
class C extends Null { }
|
||||
class Test extends Enum {
|
||||
constructor (value?: string, index?: number) {
|
||||
super({
|
||||
a: A,
|
||||
b: B,
|
||||
c: C
|
||||
}, value, index);
|
||||
}
|
||||
}
|
||||
|
||||
expect(new Test().toJSON()).toEqual({ a: null });
|
||||
});
|
||||
|
||||
it('creates via with', () => {
|
||||
class A extends Null { }
|
||||
class B extends U32 { }
|
||||
class C extends Null { }
|
||||
const Test = Enum.with({ A, B, C });
|
||||
|
||||
expect(new Test().toJSON()).toEqual({ A: null });
|
||||
expect(new Test(1234, 1).toJSON()).toEqual({ B: 1234 });
|
||||
});
|
||||
|
||||
// We are currently not using this approach, none of the types in Substrate currently
|
||||
// have any overrides. Instead of trying to support it (just-in-case), rather have it
|
||||
// removed to simplify the code - it can be pulled-back if needed
|
||||
it.skip('allows checking against defined indexes', () => {
|
||||
expect(
|
||||
new Enum(
|
||||
{ 1: Text, 5: U32 },
|
||||
new Uint8Array([1, 2 << 2, 49, 50])
|
||||
).toString()
|
||||
).toEqual('Text');
|
||||
});
|
||||
|
||||
it('allows accessing the type and value', () => {
|
||||
const text = new Text('foo');
|
||||
const enumType = new Enum(
|
||||
{ Text, U32 },
|
||||
{ Text: text }
|
||||
);
|
||||
|
||||
expect(enumType.type).toBe('Text');
|
||||
expect(enumType.value).toEqual(text);
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
const eqtest = new Enum(
|
||||
{ U32, Text },
|
||||
new Uint8Array([1, 3 << 2, 88, 89, 90])
|
||||
);
|
||||
|
||||
it('compares against index', () => {
|
||||
expect(
|
||||
eqtest.eq(1)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('compares against values', () => {
|
||||
expect(
|
||||
eqtest.eq('XYZ')
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('string-only construction (old Enum)', () => {
|
||||
const testDecode = (type: string, input: any, expected: any) =>
|
||||
it(`can decode from ${type}`, () => {
|
||||
const e = new Enum(['foo', 'bar'], input);
|
||||
|
||||
expect(e.toString()).toBe(expected);
|
||||
});
|
||||
|
||||
const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any) =>
|
||||
it(`can encode ${to}`, () => {
|
||||
const e = new Enum(['foo', 'bar'], 1);
|
||||
|
||||
expect(e[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
testDecode('Enum', undefined, 'foo');
|
||||
testDecode('Enum', new Enum(['foo', 'bar'], 1), 'bar');
|
||||
testDecode('number', 0, 'foo');
|
||||
testDecode('number', 1, 'bar');
|
||||
testDecode('string', 'bar', 'bar');
|
||||
testDecode('Uint8Array', Uint8Array.from([0]), 'foo');
|
||||
testDecode('Uint8Array', Uint8Array.from([1]), 'bar');
|
||||
|
||||
testEncode('toJSON', 1);
|
||||
testEncode('toNumber', 1);
|
||||
testEncode('toString', 'bar');
|
||||
testEncode('toU8a', Uint8Array.from([1]));
|
||||
|
||||
it('provides a clean toString()', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar']).toString()
|
||||
).toEqual('foo');
|
||||
});
|
||||
|
||||
it('provides a clean toString() (enum)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], new Enum(['foo', 'bar'], 1)).toNumber()
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it('converts to and from U8a', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], new Uint8Array([1])).toU8a()
|
||||
).toEqual(new Uint8Array([1]));
|
||||
});
|
||||
|
||||
it('converts from JSON', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar', 'baz', 'gaz', 'jaz'], 4).toNumber()
|
||||
).toEqual(4);
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
it('compares agains the index value', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq(1)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('compares agains the index value (false)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq(0)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('compares agains the string value', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq('bar')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('compares agains the string value (false)', () => {
|
||||
expect(
|
||||
new Enum(['foo', 'bar'], 1).eq('foo')
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Codec, Constructor, ConstructorDef } from '../types';
|
||||
import { Codec, Constructor } from '../types';
|
||||
|
||||
import { assert, hexToU8a, isHex, isNumber, isObject, isString, isU8a, isUndefined, u8aConcat, u8aToHex } from '@polkadot/util';
|
||||
|
||||
@@ -20,12 +20,8 @@ type Decoded = {
|
||||
value: Codec
|
||||
};
|
||||
|
||||
type Aliasses = {
|
||||
[index: string]: string
|
||||
};
|
||||
|
||||
/**
|
||||
* @name EnumType
|
||||
* @name Enum
|
||||
* @description
|
||||
* This implements an enum, that based on the value wraps a different type. It is effectively
|
||||
* an extension to enum where the value type is determined by the actual index.
|
||||
@@ -33,80 +29,93 @@ type Aliasses = {
|
||||
// TODO:
|
||||
// - As per Enum, actually use TS enum
|
||||
// - It should rather probably extend Enum instead of copying code
|
||||
export default class EnumType<T> extends Base<Codec> implements Codec {
|
||||
export default class Enum extends Base<Codec> implements Codec {
|
||||
private _def: TypesDef;
|
||||
private _index: number;
|
||||
private _indexes: Array<number>;
|
||||
private _isBasic: boolean;
|
||||
|
||||
constructor (def: TypesDef, value?: any, index?: number | EnumType<T>, aliasses?: Aliasses) {
|
||||
const decoded = EnumType.decodeEnumType(def, aliasses || {}, value, index);
|
||||
constructor (def: TypesDef | Array<string>, value?: any, index?: number | Enum) {
|
||||
const defInfo = Enum.extractDef(def);
|
||||
const decoded = Enum.decodeEnum(defInfo.def, value, index);
|
||||
|
||||
super(decoded.value);
|
||||
|
||||
this._def = def;
|
||||
this._indexes = Object.keys(def).map((_, index) => index);
|
||||
this._def = defInfo.def;
|
||||
this._isBasic = defInfo.isBasic;
|
||||
this._indexes = Object.keys(defInfo.def).map((_, index) => index);
|
||||
this._index = this._indexes.indexOf(decoded.index) || 0;
|
||||
}
|
||||
|
||||
private static decodeEnumType<T> (def: TypesDef, aliasses: Aliasses, value?: any, index?: number | EnumType<T>): Decoded {
|
||||
private static extractDef (def: TypesDef | Array<string>): { def: TypesDef, isBasic: boolean } {
|
||||
if (!Array.isArray(def)) {
|
||||
return {
|
||||
def,
|
||||
isBasic: false
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
def: def.reduce((def, key) => {
|
||||
def[key] = Null;
|
||||
|
||||
return def;
|
||||
}, {} as TypesDef),
|
||||
isBasic: true
|
||||
};
|
||||
}
|
||||
|
||||
private static decodeEnum (def: TypesDef, value?: any, index?: number | Enum): Decoded {
|
||||
// If `index` is set, we parse it.
|
||||
if (index instanceof EnumType) {
|
||||
return EnumType.createValue(def, index._index, index.raw);
|
||||
if (index instanceof Enum) {
|
||||
return Enum.createValue(def, index._index, index.raw);
|
||||
} else if (isNumber(index)) {
|
||||
return EnumType.createValue(def, index, value);
|
||||
return Enum.createValue(def, index, value);
|
||||
}
|
||||
|
||||
// Or else, we just look at `value`
|
||||
return EnumType.decodeViaValue(def, aliasses, value);
|
||||
return Enum.decodeViaValue(def, value);
|
||||
}
|
||||
|
||||
private static decodeViaValue (def: TypesDef, aliasses: Aliasses, value?: any): Decoded {
|
||||
if (value instanceof EnumType) {
|
||||
return EnumType.createValue(def, value._index, value.raw);
|
||||
private static decodeViaValue (def: TypesDef, value?: any): Decoded {
|
||||
if (value instanceof Enum) {
|
||||
return Enum.createValue(def, value._index, value.raw);
|
||||
} else if (isU8a(value)) {
|
||||
return EnumType.createValue(def, value[0], value.subarray(1));
|
||||
return Enum.createValue(def, value[0], value.subarray(1));
|
||||
} else if (isNumber(value)) {
|
||||
return EnumType.createValue(def, value);
|
||||
return Enum.createValue(def, value);
|
||||
} else if (isString(value)) {
|
||||
const _str = value.toString();
|
||||
|
||||
return isHex(_str)
|
||||
? EnumType.decodeViaValue(def, aliasses, hexToU8a(_str))
|
||||
: EnumType.createViaJSON(def, aliasses, _str);
|
||||
? Enum.decodeViaValue(def, hexToU8a(_str))
|
||||
: Enum.createViaJSON(def, _str);
|
||||
} else if (isObject(value)) {
|
||||
const key = Object.keys(value)[0];
|
||||
|
||||
return EnumType.createViaJSON(def, aliasses, key, value[key]);
|
||||
return Enum.createViaJSON(def, key, value[key]);
|
||||
}
|
||||
|
||||
// Worst-case scenario, return the first with default
|
||||
return EnumType.createValue(def, 0);
|
||||
return Enum.createValue(def, 0);
|
||||
}
|
||||
|
||||
private static createViaJSON (def: TypesDef, _aliasses: Aliasses, key: string, value?: any) {
|
||||
private static createViaJSON (def: TypesDef, key: string, value?: any) {
|
||||
// JSON comes in the form of { "<type (lowercased)>": "<value for type>" }, here we
|
||||
// additionally force to lower to ensure forward compat
|
||||
const keys = Object.keys(def).map((k) => k.toLowerCase());
|
||||
|
||||
// FIXME This whole lookup process is not vey efficient
|
||||
const keyLower = key.toLowerCase();
|
||||
const aliasses = Object.keys(_aliasses).reduce((aliasses, a) => {
|
||||
aliasses[a.toLowerCase()] = _aliasses[a].toLowerCase();
|
||||
const index = keys.indexOf(keyLower);
|
||||
|
||||
return aliasses;
|
||||
}, {} as Aliasses);
|
||||
const aliasKey = aliasses[keyLower] || keyLower;
|
||||
const index = keys.indexOf(aliasKey);
|
||||
assert(index !== -1, `Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`);
|
||||
|
||||
assert(index !== -1, `Cannot map EnumType JSON, unable to find '${key}' in ${keys.join(', ')}`);
|
||||
|
||||
return EnumType.createValue(def, index, value);
|
||||
return Enum.createValue(def, index, value);
|
||||
}
|
||||
|
||||
private static createValue (def: TypesDef, index: number = 0, value?: any): Decoded {
|
||||
const Clazz = Object.values(def)[index];
|
||||
|
||||
assert(!isUndefined(Clazz), `Unable to create EnumType via index ${index}, in ${Object.keys(def).join(', ')}`);
|
||||
assert(!isUndefined(Clazz), `Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`);
|
||||
|
||||
return {
|
||||
index,
|
||||
@@ -114,10 +123,8 @@ export default class EnumType<T> extends Base<Codec> implements Codec {
|
||||
};
|
||||
}
|
||||
|
||||
static with<
|
||||
S extends ConstructorDef
|
||||
> (Types: S): EnumConstructor<EnumType<S>> {
|
||||
return class extends EnumType<S> {
|
||||
static with (Types: TypesDef | Array<string>): EnumConstructor<Enum> {
|
||||
return class extends Enum {
|
||||
constructor (value?: any, index?: number) {
|
||||
super(Types, value, index);
|
||||
}
|
||||
@@ -180,6 +187,8 @@ export default class EnumType<T> extends Base<Codec> implements Codec {
|
||||
// cater for the case where we only pass the enum index
|
||||
if (isNumber(other)) {
|
||||
return this.toNumber() === other;
|
||||
} else if (this._isBasic && isString(other)) {
|
||||
return this.type === other;
|
||||
}
|
||||
|
||||
// compare the actual wrapper value
|
||||
@@ -197,9 +206,9 @@ export default class EnumType<T> extends Base<Codec> implements Codec {
|
||||
* @description Converts the Object to JSON, typically used for RPC transfers
|
||||
*/
|
||||
toJSON (): any {
|
||||
return {
|
||||
[this.type]: this.raw.toJSON()
|
||||
};
|
||||
return this._isBasic
|
||||
? this._index
|
||||
: { [this.type]: this.raw.toJSON() };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Codec, Constructor } from '../types';
|
||||
import Null from '../primitive/Null';
|
||||
import Text from '../primitive/Text';
|
||||
import Compact from './Compact';
|
||||
import EnumType from './EnumType';
|
||||
import Enum from './Enum';
|
||||
import Linkage from './Linkage';
|
||||
import Option from './Option';
|
||||
import Struct from './Struct';
|
||||
@@ -172,7 +172,7 @@ export function getTypeClass (value: TypeDef): Constructor {
|
||||
case TypeDefInfo.Enum:
|
||||
assert(value.sub && Array.isArray(value.sub), 'Expected subtype for Enum');
|
||||
|
||||
return EnumType.with(
|
||||
return Enum.with(
|
||||
(value.sub as Array<TypeDef>).reduce((result, sub, index) => {
|
||||
result[sub.name as string] = getTypeClass(sub);
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"method": "author_extrinsicUpdate",
|
||||
"params": {
|
||||
"result": {
|
||||
"finalised": "0xc465b92a72b1d20918d64cd4effa70c2bb58b53a3f8c24c3ac8fd8f465f059b4"
|
||||
"finalized": "0xc465b92a72b1d20918d64cd4effa70c2bb58b53a3f8c24c3ac8fd8f465f059b4"
|
||||
},
|
||||
"subscription": 9
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Vector from '../codec/Vector';
|
||||
import Hash from './Hash';
|
||||
@@ -31,9 +31,9 @@ export class Finalization extends Null {
|
||||
/**
|
||||
* @name Phase
|
||||
* @description
|
||||
* An [[EnumType]] that indicates the specific phase where the [[EventRecord]] was generated
|
||||
* An [[Enum]] that indicates the specific phase where the [[EventRecord]] was generated
|
||||
*/
|
||||
export class Phase extends EnumType<ApplyExtrinsic | Finalization> {
|
||||
export class Phase extends Enum {
|
||||
constructor (value: any, index?: number) {
|
||||
super({
|
||||
ApplyExtrinsic,
|
||||
|
||||
@@ -6,7 +6,13 @@ import Enum from '../codec/Enum';
|
||||
|
||||
export default class StorageHasher extends Enum {
|
||||
constructor (value?: any) {
|
||||
super(['Blake2_128', 'Blake2_256', 'Twox128', 'Twox256', 'Twox128Concat'], value);
|
||||
super([
|
||||
'Blake2_128',
|
||||
'Blake2_256',
|
||||
'Twox128',
|
||||
'Twox256',
|
||||
'Twox128Concat'
|
||||
], value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Tuple from '../codec/Tuple';
|
||||
import Vector from '../codec/Vector';
|
||||
@@ -119,10 +119,9 @@ export class Consensus extends Tuple {
|
||||
/**
|
||||
* @name DigestItem
|
||||
* @description
|
||||
* A [[EnumType]] the specifies the specific item in the logs of a [[Digest]]
|
||||
* A [[Enum]] the specifies the specific item in the logs of a [[Digest]]
|
||||
*/
|
||||
export class DigestItem extends EnumType<AuthoritiesChange | ChangesTrieRoot | Other
|
||||
| Seal> {
|
||||
export class DigestItem extends Enum {
|
||||
constructor (value: any) {
|
||||
super({
|
||||
Other, // Position 0, as per Rust (encoding control)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Vector from '../codec/Vector';
|
||||
import Hash from '../primitive/Hash';
|
||||
import Null from '../primitive/Null';
|
||||
@@ -69,9 +69,9 @@ export class Usurped extends Hash {
|
||||
/**
|
||||
* @name ExtrinsicStatus
|
||||
* @description
|
||||
* An [[EnumType]] that indicates the status of the [[Extrinsic]] as been submitted
|
||||
* An [[Enum]] that indicates the status of the [[Extrinsic]] as been submitted
|
||||
*/
|
||||
export default class ExtrinsicStatus extends EnumType<Future | Ready | Finalized | Usurped | Broadcast | Dropped | Invalid> {
|
||||
export default class ExtrinsicStatus extends Enum {
|
||||
constructor (value: any, index?: number) {
|
||||
super({
|
||||
Future,
|
||||
@@ -81,9 +81,7 @@ export default class ExtrinsicStatus extends EnumType<Future | Ready | Finalized
|
||||
Broadcast,
|
||||
Dropped,
|
||||
Invalid
|
||||
}, value, index, {
|
||||
'Finalised': 'Finalized' // old UK spelling to US spelling
|
||||
});
|
||||
}, value, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Tuple from '../codec/Tuple';
|
||||
import Vector from '../codec/Vector';
|
||||
@@ -53,7 +53,7 @@ export class ExplicitCandidateSignature extends CandidateSignature {
|
||||
export class ImplicitCandidateSignature extends CandidateSignature {
|
||||
}
|
||||
|
||||
export class ValidityAttestation extends EnumType<Null | ImplicitCandidateSignature | ExplicitCandidateSignature> {
|
||||
export class ValidityAttestation extends Enum {
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
// This Null is not in the original, however indexes start at 1, so add a
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Bytes from '../primitive/Bytes';
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Hash from '../primitive/Hash';
|
||||
import U64 from '../primitive/U64';
|
||||
@@ -62,7 +62,7 @@ export class Tombstone extends TombstoneContractInfo {
|
||||
* @description
|
||||
* The contract information for a given contract
|
||||
*/
|
||||
export default class ContractInfo extends EnumType<Alive | Tombstone> {
|
||||
export default class ContractInfo extends Enum {
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
Alive,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AnyNumber } from '../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import EnumType from '../codec/EnumType';
|
||||
import Enum from '../codec/Enum';
|
||||
import Struct from '../codec/Struct';
|
||||
import Hash from '../primitive/Hash';
|
||||
import U32 from '../primitive/U32';
|
||||
@@ -118,9 +118,9 @@ export class BftDoubleCommit extends BftAtReport {
|
||||
/**
|
||||
* @name MisbehaviorKind
|
||||
* @description
|
||||
* An [[EnumType]] containing a Bft misbehaviour
|
||||
* An [[Enum]] containing a Bft misbehaviour
|
||||
*/
|
||||
export class MisbehaviorKind extends EnumType<BftProposeOutOfTurn | BftDoublePropose | BftDoublePrepare | BftDoubleCommit> {
|
||||
export class MisbehaviorKind extends Enum {
|
||||
constructor (value?: BftAtReportValue | Uint8Array, index?: number) {
|
||||
super({
|
||||
BftProposeOutOfTurn,
|
||||
|
||||
Reference in New Issue
Block a user