From b6dd43ee71771c6353b9a01970cc7215a114f5de Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 20 Nov 2018 12:45:56 +0100 Subject: [PATCH] Vector extends Array (#404) * Vector extends Array * Update tests * Add tests for Vector * Remove .get on Vector * Change back toString representation --- packages/types/src/Bft.spec.ts | 2 +- packages/types/src/Digest.spec.ts | 2 +- packages/types/src/EventRecord.spec.ts | 4 +- packages/types/src/RuntimeVersion.spec.ts | 2 +- packages/types/src/SignedBlock.spec.ts | 8 +-- packages/types/src/StorageChangeSet.spec.ts | 2 +- .../codec/{Vector.spec.js => Vector.spec.ts} | 55 +++++++++------ packages/types/src/codec/Vector.ts | 68 +++++++------------ 8 files changed, 71 insertions(+), 72 deletions(-) rename packages/types/src/codec/{Vector.spec.js => Vector.spec.ts} (52%) diff --git a/packages/types/src/Bft.spec.ts b/packages/types/src/Bft.spec.ts index 3429f46b6f..61f46c709d 100644 --- a/packages/types/src/Bft.spec.ts +++ b/packages/types/src/Bft.spec.ts @@ -53,7 +53,7 @@ describe('Justification', () => { }); it('has the correct signatures', () => { - const sig = just.signatures.get(1); + const sig = just.signatures[1]; expect(sig.authorityId.toHex()).toEqual('0x9876'); }); diff --git a/packages/types/src/Digest.spec.ts b/packages/types/src/Digest.spec.ts index 0ff52a52c5..fe101b8123 100644 --- a/packages/types/src/Digest.spec.ts +++ b/packages/types/src/Digest.spec.ts @@ -11,7 +11,7 @@ describe('Digest', () => { expect(digest.logs.length).toEqual(1); - const log = digest.logs.get(0); + const log = digest.logs[0]; expect(log.type).toBe('Seal'); expect(log.value.toJSON()).toEqual([ diff --git a/packages/types/src/EventRecord.spec.ts b/packages/types/src/EventRecord.spec.ts index eea014144d..8f9f31a057 100644 --- a/packages/types/src/EventRecord.spec.ts +++ b/packages/types/src/EventRecord.spec.ts @@ -21,7 +21,7 @@ describe('EventRecord', () => { it('decodes correctly', () => { const records: Vector = createType('Vec', json1.params.result.changes[0][1]) as any; - const er = records.get(0); + const er = records[0]; expect(er.phase.type).toEqual('ApplyExtrinsic'); }); @@ -31,7 +31,7 @@ describe('EventRecord', () => { expect(records).toHaveLength(4); - const er = records.get(2); + const er = records[2]; expect( er.event.data.toArray().map((v) => v.toString()) diff --git a/packages/types/src/RuntimeVersion.spec.ts b/packages/types/src/RuntimeVersion.spec.ts index a31c2d7526..e34c053cba 100644 --- a/packages/types/src/RuntimeVersion.spec.ts +++ b/packages/types/src/RuntimeVersion.spec.ts @@ -13,7 +13,7 @@ describe('RuntimeVersion', () => { }); it('has the apis', () => { - const api = version.apis.get(0); + const api = version.apis[0]; expect(api.id.toHex()).toEqual('0x696e686572656e74'); expect(api.version.toNumber()).toEqual(1); diff --git a/packages/types/src/SignedBlock.spec.ts b/packages/types/src/SignedBlock.spec.ts index 120305625b..f2bf05a177 100644 --- a/packages/types/src/SignedBlock.spec.ts +++ b/packages/types/src/SignedBlock.spec.ts @@ -35,8 +35,8 @@ describe('SignedBlock', () => { const s = new SignedBlock(events.result); expect(s.block.header.digest.logs.length).toBe(1); - expect(s.block.header.digest.logs.get(0).type).toBe('AuthoritiesChange'); - expect(s.block.header.digest.logs.get(0).value.toJSON()).toEqual([ + expect(s.block.header.digest.logs[0].type).toBe('AuthoritiesChange'); + expect(s.block.header.digest.logs[0].value.toJSON()).toEqual([ '5EyrU8k6worwXWfW2LajRvS7XF6eta286tMpHeHUwMPpzQ9F', '5Dpq7rU6KAZVdRtcegvpCoeJtZzFPzieRkRz4xajRAiMRkCf', '5CCtQPAYtuDXt6H4WMrn5nFNk4gemkVRD3k1r1mA7YiZLPBq', @@ -55,14 +55,14 @@ describe('SignedBlock', () => { }); it('has the correct data for the first', () => { - const x = extrinsics.get(0); + const x = extrinsics[0]; expect(x.callIndex).toEqual(new Uint8Array([2, 0])); expect(x.method.args[0].toU8a()).toEqual(new Uint8Array([70, 41, 195, 91, 0, 0, 0, 0])); }); it('has the correct data for the second', () => { - const x = extrinsics.get(1); + const x = extrinsics[1]; expect(x.callIndex).toEqual(new Uint8Array([1, 0])); expect(x.method.args[0].toString()).toEqual('5CPaGq4KcmntaboAxg5nyqGXdyzaBV2hj6PvhNA3syigiRg8'); diff --git a/packages/types/src/StorageChangeSet.spec.ts b/packages/types/src/StorageChangeSet.spec.ts index d5f44b78ae..1055366e58 100644 --- a/packages/types/src/StorageChangeSet.spec.ts +++ b/packages/types/src/StorageChangeSet.spec.ts @@ -20,7 +20,7 @@ describe('StorageChangeSet', () => { it('wraps key/value', () => { expect( - set.changes.get(0).value.toString() + set.changes[0].value.toString() ).toEqual('0xcd'); }); }); diff --git a/packages/types/src/codec/Vector.spec.js b/packages/types/src/codec/Vector.spec.ts similarity index 52% rename from packages/types/src/codec/Vector.spec.js rename to packages/types/src/codec/Vector.spec.ts index 7d96e35d1c..a8fb08af05 100644 --- a/packages/types/src/codec/Vector.spec.js +++ b/packages/types/src/codec/Vector.spec.ts @@ -4,30 +4,35 @@ import extrinsics from '@polkadot/extrinsics/static'; +import AccountId from '../AccountId'; +import { Codec, CodecTo } from '../types'; import createType from './createType'; import Method from '../Method'; +import PropIndex from '../PropIndex'; +import Proposal from '../Proposal'; import Text from '../Text'; import Vector from './Vector'; +import Tuple from './Tuple'; describe('Vector', () => { - let array; + let vector: Vector; beforeEach(() => { - array = new Vector(Text, ['1', '23', '345', '4567', new Text('56789')]); + vector = new Vector(Text, ['1', '23', '345', '4567', new Text('56789')]); Method.injectExtrinsics(extrinsics); }); it('wraps a sequence of values', () => { - expect(array.length).toEqual(5); // eslint-disable-line + expect(vector.length).toEqual(5); // eslint-disable-line }); it('has a sane representation for toString', () => { - expect(array.toString()).toEqual('[1, 23, 345, 4567, 56789]'); + expect(vector.toString()).toEqual('[1, 23, 345, 4567, 56789]'); }); it('encodes with length prefix', () => { - expect(array.toU8a()).toEqual(new Uint8Array([ + expect(vector.toU8a()).toEqual(new Uint8Array([ 5 << 2, 1 << 2, 49, 2 << 2, 50, 51, @@ -39,36 +44,36 @@ describe('Vector', () => { it('allows contruction via JSON', () => { expect( - new Vector(Text, ['6', '7']).toString() - ).toEqual('[6, 7]'); + new Vector(Text, ['6', '7']).toJSON() + ).toEqual(['6', '7']); }); it('exposes the type', () => { - expect(array.Type).toEqual('Text'); + expect(vector.Type).toEqual('Text'); }); it('decodes a complex type via construction', () => { const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([ 4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79 ])); - const first = test.get(0); + const first = (test as Vector)[0] as Tuple; - expect(first.getAtIndex(0).toNumber()).toEqual(10); - expect(first.getAtIndex(1).callIndex).toEqual(new Uint8Array([0, 3])); - expect(first.getAtIndex(2).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ'); + expect((first.getAtIndex(0) as PropIndex).toNumber()).toEqual(10); + expect((first.getAtIndex(1) as Proposal).callIndex).toEqual(new Uint8Array([0, 3])); + expect((first.getAtIndex(2) as AccountId).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ'); }); - describe('array-like functions', () => { + describe('vector-like functions', () => { it('allows retrieval of a specific item', () => { expect( - array.get(2).toString() + vector[2].toString() ).toEqual('345'); }); it('exposes a working forEach', () => { - const result = {}; + const result: { [index: number]: string } = {}; - array.forEach((e, i) => { + vector.forEach((e, i) => { result[i] = e.toString(); }); @@ -83,20 +88,32 @@ describe('Vector', () => { it('exposes a working filter', () => { expect( - array.filter((e, i) => i >= 3).toString() + vector.filter((e, i) => i >= 3).toString() ).toEqual('4567,56789'); }); it('exposes a working map', () => { expect( - array.map((e) => e.toString().substr(0, 1)) + vector.map((e) => e.toString().substr(0, 1)) ).toEqual(['1', '2', '3', '4', '5']); }); it('exposes a working reduce', () => { expect( - array.reduce((r, e) => `${r}${e}`, '') + vector.reduce((r, e) => `${r}${e}`, '') ).toEqual('123345456756789'); }); }); + + describe('encode', () => { + const testEncode = (to: CodecTo, expected: any) => + it(`can encode ${to}`, () => { + expect(vector[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x1404310832330c3334351034353637143536373839'); + testEncode('toJSON', ['1', '23', '345', '4567', '56789']); + testEncode('toString', '[1, 23, 345, 4567, 56789]'); + testEncode('toU8a', Uint8Array.from([20, 4, 49, 8, 50, 51, 12, 51, 52, 53, 16, 52, 53, 54, 55, 20, 53, 54, 55, 56, 57])); + }); }); diff --git a/packages/types/src/codec/Vector.ts b/packages/types/src/codec/Vector.ts index 7e3f369ff7..5822363810 100644 --- a/packages/types/src/codec/Vector.ts +++ b/packages/types/src/codec/Vector.ts @@ -4,7 +4,6 @@ import { u8aConcat, u8aToU8a, u8aToHex } from '@polkadot/util'; -import Base from './Base'; import Compact, { DEFAULT_LENGTH_BITS } from './Compact'; import { Codec, Constructor } from '../types'; @@ -15,12 +14,12 @@ import { Codec, Constructor } from '../types'; // `reduce` is exposed on the interface. export default class Vector< T extends Codec - > extends Base> implements Codec { + > extends Array implements Codec { private _Type: Constructor; constructor (Type: Constructor, value: Vector | Uint8Array | string | Array = [] as Array) { super( - Vector.decodeVector(Type, value) + ...Vector.decodeVector(Type, value) ); this._Type = Type; @@ -33,8 +32,6 @@ export default class Vector< ? entry : new Type(entry) ); - } else if (value instanceof Vector) { - return Vector.decodeVector(Type, value.raw); } const u8a = u8aToU8a(value); @@ -66,42 +63,14 @@ export default class Vector< return this._Type.name; } - get length (): number { - return this.raw.length; - } - get encodedLength (): number { - return this.raw.reduce((total, raw) => { + return this.reduce((total, raw) => { return total + raw.encodedLength; }, Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length); } - filter (fn: (item: T, index: number) => any): Array { - return this.raw.filter(fn); - } - - find (fn: (item: T, index: number) => any): T | undefined { - return this.raw.find(fn); - } - - forEach (fn: (item: T, index: number) => any): any { - return this.raw.forEach(fn); - } - - get (index: number): T { - return this.raw[index]; - } - - map (fn: (item: T, index: number) => O): Array { - return this.raw.map(fn); - } - - push (item: T): void { - this.raw.push(item); - } - - reduce (fn: (result: O, item: T, index: number) => O, initial: O): O { - return this.raw.reduce(fn, initial); + toArray (): Array { + return Array.from(this); } toHex (): string { @@ -109,13 +78,22 @@ export default class Vector< } toJSON (): any { - return this.raw.map((entry) => + return this.map((entry) => entry.toJSON() ); } + toString (): string { + // Overwrite the default toString representation of Array. + const data = this.map((entry) => + entry.toString() + ); + + return `[${data.join(', ')}]`; + } + toU8a (isBare?: boolean): Uint8Array { - const encoded = this.raw.map((entry) => + const encoded = this.map((entry) => entry.toU8a(isBare) ); @@ -127,11 +105,15 @@ export default class Vector< ); } - toString (): string { - const data = this.raw.map((entry) => - entry.toString() - ); + // Below are methods that we override. When we do a `new Vector(...).map()`, + // we want it to return an Array. We only override the methods that return a + // new instance. - return `[${data.join(', ')}]`; + filter (callbackfn: (value: T, index: number, array: Array) => any, thisArg?: any): Array { + return this.toArray().filter(callbackfn, thisArg); + } + + map (callbackfn: (value: T, index: number, array: Array) => U, thisArg?: any): Array { + return this.toArray().map(callbackfn, thisArg); } }