class Struct extends Map (#321)

* Rename to getAtIndex

* Add codec interface

* Fix stuff

* Fix struct

* Allow ts

* Add useful types

* Fix related types

* Fix test

* Small fix

* Use this.get everywhere

* Fix import

* Typos

* Fix ts ignore

* Add more tests

* Update polkadot dev

* Don't change jest config

* Fix eol
This commit is contained in:
Amaury Martiny
2018-10-19 14:49:09 +02:00
committed by Jaco Greeff
parent 174978a072
commit f2287f38c5
20 changed files with 242 additions and 208 deletions
+7 -7
View File
@@ -28,11 +28,11 @@ export class BftAuthoritySignature extends Tuple {
}
get authorityId (): AuthorityId {
return this.raw.authorityId as AuthorityId;
return this.getAtIndex(0) as AuthorityId;
}
get signature (): Signature {
return this.raw.signature as Signature;
return this.getAtIndex(1) as Signature;
}
}
@@ -52,11 +52,11 @@ export class BftHashSignature extends Tuple {
}
get hash (): Hash {
return this.raw.hash as Hash;
return this.getAtIndex(0) as Hash;
}
get signature (): Signature {
return this.raw.signature as Signature;
return this.getAtIndex(1) as Signature;
}
}
@@ -78,14 +78,14 @@ export class Justification extends Struct {
}
get hash (): Hash {
return this.raw.hash as Hash;
return this.get('hash') as Hash;
}
get round (): U32 {
return this.raw.round as U32;
return this.get('round') as U32;
}
get signatures (): Vector<BftAuthoritySignature> {
return this.raw.signatures as Vector<BftAuthoritySignature>;
return this.get('signatures') as Vector<BftAuthoritySignature>;
}
}
+2 -2
View File
@@ -26,7 +26,7 @@ export default class Block extends Struct {
}
get extrinsics (): Extrinsics {
return this.raw.extrinsics as Extrinsics;
return this.get('extrinsics') as Extrinsics;
}
// convenience, encodes the header and returns the actual hash
@@ -37,6 +37,6 @@ export default class Block extends Struct {
}
get header (): Header {
return this.raw.header as Header;
return this.get('header') as Header;
}
}
+2 -2
View File
@@ -94,11 +94,11 @@ export default class Extrinsic extends Struct {
}
get method (): Method {
return this.raw.method as Method;
return this.get('method') as Method;
}
get signature (): ExtrinsicSignature {
return this.raw.signature as ExtrinsicSignature;
return this.get('signature') as ExtrinsicSignature;
}
get encodedLength (): number {
+8 -8
View File
@@ -74,19 +74,19 @@ export default class ExtrinsicSignature extends Struct {
}
get era (): ExtrinsicEra {
return this.raw.era as ExtrinsicEra;
return this.get('era') as ExtrinsicEra;
}
get nonce (): Nonce {
return this.raw.nonce as Nonce;
return this.get('nonce') as Nonce;
}
get signature (): Signature {
return this.raw.signature as Signature;
return this.get('signature') as Signature;
}
get signer (): Address {
return this.raw.signer as Address;
return this.get('signer') as Address;
}
get version (): number {
@@ -112,10 +112,10 @@ export default class ExtrinsicSignature extends Struct {
});
const signature = new Signature(signingPayload.sign(signerPair));
this.raw.era = signingPayload.era;
this.raw.nonce = signingPayload.nonce;
this.raw.signer = signer;
this.raw.signature = signature;
this.set('era', signingPayload.era);
this.set('nonce', signingPayload.nonce);
this.set('signer', signer);
this.set('signature', signature);
return this;
}
+6 -6
View File
@@ -36,7 +36,7 @@ export class Digest extends Struct {
}
get logs (): Vector<Bytes> {
return this.raw.logs as Vector<Bytes>;
return this.get('logs') as Vector<Bytes>;
}
}
@@ -53,15 +53,15 @@ export default class Header extends Struct {
}
get blockNumber (): BlockNumber {
return this.raw.number as BlockNumber;
return this.get('number') as BlockNumber;
}
get digest (): Digest {
return this.raw.digest as Digest;
return this.get('digest') as Digest;
}
get extrinsicsRoot (): Hash {
return this.raw.extrinsicsRoot as Hash;
return this.get('extrinsicsRoot') as Hash;
}
// convenience, encodes the header and returns the actual hash
@@ -72,10 +72,10 @@ export default class Header extends Struct {
}
get parentHash (): Hash {
return this.raw.parentHash as Hash;
return this.get('parentHash') as Hash;
}
get stateRoot (): Hash {
return this.raw.stateRoot as Hash;
return this.get('stateRoot') as Hash;
}
}
+4 -4
View File
@@ -28,11 +28,11 @@ export default class KeyValue extends Struct {
}
get key (): StorageKey {
return this.raw.key as StorageKey;
return this.get('key') as StorageKey;
}
get value (): StorageData {
return this.raw.value as StorageData;
return this.get('value') as StorageData;
}
}
@@ -53,10 +53,10 @@ export class KeyValueOption extends Tuple {
}
get key (): StorageKey {
return this.raw.key as StorageKey;
return this.getAtIndex(0) as StorageKey;
}
get value (): Option<StorageData> {
return this.raw.value as Option<StorageData>;
return this.getAtIndex(1) as Option<StorageData>;
}
}
+34 -34
View File
@@ -32,15 +32,15 @@ export class OuterDispatchCall extends Struct {
}
get index (): U16 {
return this.raw.index as U16;
return this.get('index') as U16;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
get prefix (): Text {
return this.raw.prefix as Text;
return this.get('prefix') as Text;
}
}
@@ -53,11 +53,11 @@ export class OuterDispatchMetadata extends Struct {
}
get calls (): Vector<OuterDispatchCall> {
return this.raw.calls as Vector<OuterDispatchCall>;
return this.get('calls') as Vector<OuterDispatchCall>;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -71,15 +71,15 @@ export class EventMetadata extends Struct {
}
get arguments (): Vector<Type> {
return this.raw.arguments as Vector<Type>;
return this.get('arguments') as Vector<Type>;
}
get documentation (): Vector<Text> {
return this.raw.documentation as Vector<Text>;
return this.get('documentation') as Vector<Text>;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -109,11 +109,11 @@ export class OuterEventMetadata extends Struct {
}
get events (): Vector<OuterEventMetadataEvent> {
return this.raw.events as Vector<OuterEventMetadataEvent>;
return this.get('events') as Vector<OuterEventMetadataEvent>;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -126,11 +126,11 @@ export class FunctionArgumentMetadata extends Struct {
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
get type (): Type {
return this.raw.type as Type;
return this.get('type') as Type;
}
}
@@ -145,19 +145,19 @@ export class FunctionMetadata extends Struct {
}
get arguments (): Vector<FunctionArgumentMetadata> {
return this.raw.arguments as Vector<FunctionArgumentMetadata>;
return this.get('arguments') as Vector<FunctionArgumentMetadata>;
}
get documentation (): Vector<Text> {
return this.raw.documentation as Vector<Text>;
return this.get('documentation') as Vector<Text>;
}
get id (): U16 {
return this.raw.id as U16;
return this.get('id') as U16;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -170,11 +170,11 @@ export class CallMetadata extends Struct {
}
get functions (): Vector<FunctionMetadata> {
return this.raw.functions as Vector<FunctionMetadata>;
return this.get('functions') as Vector<FunctionMetadata>;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -187,11 +187,11 @@ export class ModuleMetadata extends Struct {
}
get call (): CallMetadata {
return this.raw.call as CallMetadata;
return this.get('call') as CallMetadata;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
}
@@ -210,11 +210,11 @@ export class StorageFunctionType$Map extends Struct {
}
get key (): Type {
return this.raw.type as Type;
return this.get('key') as Type;
}
get value (): Type {
return this.raw.value as Type;
return this.get('value') as Type;
}
}
@@ -263,19 +263,19 @@ export class StorageFunctionMetadata extends Struct {
}
get documentation (): Vector<Text> {
return this.raw.documentation as Vector<Text>;
return this.get('documentation') as Vector<Text>;
}
get name (): Text {
return this.raw.name as Text;
return this.get('name') as Text;
}
get modifier (): StorageFunctionModifier {
return this.raw.modifier as StorageFunctionModifier;
return this.get('modifier') as StorageFunctionModifier;
}
get type (): StorageFunctionType {
return this.raw.type as StorageFunctionType;
return this.get('type') as StorageFunctionType;
}
}
@@ -288,11 +288,11 @@ export class StorageMetadata extends Struct {
}
get functions (): Vector<StorageFunctionMetadata> {
return this.raw.functions as Vector<StorageFunctionMetadata>;
return this.get('functions') as Vector<StorageFunctionMetadata>;
}
get prefix (): Text {
return this.raw.prefix as Text;
return this.get('prefix') as Text;
}
}
@@ -306,15 +306,15 @@ export class RuntimeModuleMetadata extends Struct {
}
get module (): ModuleMetadata {
return this.raw.module as ModuleMetadata;
return this.get('module') as ModuleMetadata;
}
get prefix (): Text {
return this.raw.prefix as Text;
return this.get('prefix') as Text;
}
get storage (): StorageMetadata | undefined {
return (this.raw.storage as Option<StorageMetadata>).value;
return (this.get('storage') as Option<StorageMetadata>).value;
}
}
@@ -358,15 +358,15 @@ export default class RuntimeMetadata extends Struct {
}
get calls (): Vector<OuterDispatchCall> {
return (this.raw.outerDispatch as OuterDispatchMetadata).calls;
return (this.get('outerDispatch') as OuterDispatchMetadata).calls;
}
get events (): Vector<OuterEventMetadataEvent> {
return (this.raw.outerEvent as OuterEventMetadata).events;
return (this.get('outerEvent') as OuterEventMetadata).events;
}
get modules (): Vector<RuntimeModuleMetadata> {
return this.raw.modules as Vector<RuntimeModuleMetadata>;
return this.get('modules') as Vector<RuntimeModuleMetadata>;
}
// Helper to retrieve a list of all type that are found, sorted and de-deuplicated
+4 -3
View File
@@ -147,7 +147,8 @@ export default class Method extends Struct {
}
get args (): Array<Base> {
return (this.getAtIndex(1) as Struct<Base>).values();
// FIXME This should return a Struct instead of an Array
return [...(this.get('args') as Struct).values()];
}
get argsDef (): ArgsDef {
@@ -155,11 +156,11 @@ export default class Method extends Struct {
}
get callIndex (): Uint8Array {
return (this.getAtIndex(0) as MethodIndex).callIndex;
return (this.get('methodIndex') as MethodIndex).callIndex;
}
get data (): Uint8Array {
return (this.getAtIndex(1) as Struct<Base>).toU8a();
return (this.get('args') as Struct).toU8a();
}
get meta (): FunctionMetadata {
+7 -7
View File
@@ -34,15 +34,15 @@ export class BftAtReport extends Struct {
}
get a (): BftHashSignature {
return this.raw.a as BftHashSignature;
return this.get('a') as BftHashSignature;
}
get b (): BftHashSignature {
return this.raw.b as BftHashSignature;
return this.get('b') as BftHashSignature;
}
get round (): U32 {
return this.raw.round as U32;
return this.get('round') as U32;
}
}
@@ -80,18 +80,18 @@ export default class MisbehaviorReport extends Struct {
}
get misbehavior (): MisbehaviorKind {
return this.raw.misbehavior as MisbehaviorKind;
return this.get('misbehavior') as MisbehaviorKind;
}
get parentHash (): Hash {
return this.raw.parentHash as Hash;
return this.get('parentHash') as Hash;
}
get parentNumber (): BlockNumber {
return this.raw.parentNumber as BlockNumber;
return this.get('parentNumber') as BlockNumber;
}
get target (): AuthorityId {
return this.raw.target as AuthorityId;
return this.get('target') as AuthorityId;
}
}
+8 -8
View File
@@ -31,11 +31,11 @@ class RuntimeVersionApi extends Tuple {
}
get id (): ApiId {
return this.raw.id as ApiId;
return this.getAtIndex(0) as ApiId;
}
get version (): U32 {
return this.raw.version as U32;
return this.getAtIndex(1) as U32;
}
}
@@ -67,26 +67,26 @@ export default class RuntimeVersion extends Struct {
}
get apis (): Vector<RuntimeVersionApi> {
return this.raw.apis as Vector<RuntimeVersionApi>;
return this.get('apis') as Vector<RuntimeVersionApi>;
}
get authoringVersion (): U32 {
return this.raw.authoringVersion as U32;
return this.get('authoringVersion') as U32;
}
get implName (): Text {
return this.raw.implName as Text;
return this.get('implName') as Text;
}
get implVersion (): U32 {
return this.raw.implVersion as U32;
return this.get('implVersion') as U32;
}
get specName (): Text {
return this.raw.specName as Text;
return this.get('specName') as Text;
}
get specVersion (): U32 {
return this.raw.specVersion as U32;
return this.get('specVersion') as U32;
}
}
+4 -4
View File
@@ -40,19 +40,19 @@ export default class SignaturePayload extends Struct {
}
get blockHash (): Hash {
return this.raw.blockHash as Hash;
return this.get('blockHash') as Hash;
}
get method (): Method {
return this.raw.method as Method;
return this.get('method') as Method;
}
get era (): ExtrinsicEra {
return this.raw.era as ExtrinsicEra;
return this.get('era') as ExtrinsicEra;
}
get nonce (): Nonce {
return this.raw.nonce as Nonce;
return this.get('nonce') as Nonce;
}
get signature (): Uint8Array {
+2 -2
View File
@@ -20,10 +20,10 @@ export default class SignedBlock extends Struct {
}
get block (): Block {
return this.raw.block as Block;
return this.get('block') as Block;
}
get justification (): Justification {
return this.raw.justification as Justification;
return this.get('justification') as Justification;
}
}
+2 -2
View File
@@ -25,10 +25,10 @@ export default class StorageChangeSet extends Struct {
}
get changes (): Vector<KeyValueOption> {
return this.raw.changes as Vector<KeyValueOption>;
return this.get('changes') as Vector<KeyValueOption>;
}
get hash (): Hash {
return this.raw.hash as Hash;
return this.get('hash') as Hash;
}
}
+2 -2
View File
@@ -22,10 +22,10 @@ export default class ValidatorPrefs extends Struct {
}
get unstakeThreshold (): U32 {
return this.raw.unstakeThreshold as U32;
return this.get('unstakeThreshold') as U32;
}
get validatorPayment (): Balance {
return this.raw.validatorPayment as Balance;
return this.get('validatorPayment') as Balance;
}
}
@@ -2,26 +2,27 @@
// 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 Struct from './Struct';
import Text from '../Text';
import U32 from '../U32';
import Vector from './Vector';
const testDecode = (type, input) =>
const testDecode = (type: string, input: any) =>
it(`can decode from ${type}`, () => {
const s = new Struct({
foo: Text,
bar: U32
}, input);
expect(s.keys()).toEqual(['foo', 'bar']);
expect([...s.keys()]).toEqual(['foo', 'bar']);
expect(
s.values().map((v) =>
[...s.values()].map((v) =>
v.toString()
)
).toEqual(['bazzing', '69']);
});
const testEncode = (to, expected) =>
const testEncode = (to: CodecTo, expected: any) =>
it(`can encode ${to}`, () => {
const s = new Struct({
foo: Text,
@@ -32,13 +33,26 @@ const testEncode = (to, expected) =>
describe('Struct', () => {
testDecode('array', ['bazzing', 69]);
testDecode('hex', '0x1c62617a7a696e6745000000');
testDecode('object', { foo: 'bazzing', bar: 69 });
testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
// testEncode('toHex', '0x1c62617a7a696e6745000000'); // FIXME Add this
testEncode('toHex', '0x1c62617a7a696e6745000000');
testEncode('toJSON', { foo: 'bazzing', bar: 69 });
testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]));
testEncode('toString', '{foo: bazzing, bar: 69}');
testEncode('toString', '{"foo":"bazzing","bar":69}');
it('decodes null', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)(null).toString()
).toEqual('{}');
});
it('decodes a more complicated type', () => {
const s = new Struct({
@@ -46,8 +60,19 @@ describe('Struct', () => {
bar: Text
}))
}, { foo: [{ bar: 1 }, { bar: 2 }] });
expect(s.toString()).toBe('{foo: [{bar: 1}, {bar: 2}]}');
})
expect(s.toString()).toBe('{"foo":[{"bar":"1"},{"bar":"2"}]}');
});
it('throws when it cannot decode', () => {
expect(
() => new (
Struct.with({
txt: Text,
u32: U32
})
)('ABC')
).toThrowError(/Struct: cannot decode type/);
});
it('provides a clean toString()', () => {
expect(
@@ -57,7 +82,18 @@ describe('Struct', () => {
u32: U32
})
)({ txt: 'foo', u32: 0x123456 }).toString()
).toEqual(`{txt: foo, u32: 1193046}`);
).toEqual('{"txt":"foo","u32":1193046}');
});
it('correctly encodes length', () => {
expect(
new (
Struct.with({
txt: Text,
u32: U32
})
)({ foo: 'bazzing', bar: 69 }).encodedLength
).toEqual(5);
});
it('exposes the types', () => {
@@ -66,6 +102,10 @@ describe('Struct', () => {
foo: Text,
bar: Text,
baz: U32
}, {
foo: 'foo',
bar: 'bar',
baz: 3
}).Type
).toEqual({
foo: 'Text',
@@ -74,17 +114,16 @@ describe('Struct', () => {
});
});
it('exposes the keys/values', () => {
const test = new Struct({
foo: Text,
bar: U32
}, { foo: 'bazzing', bar: 69 });
expect(test.keys()).toEqual(['foo', 'bar']);
it('gets the value at a particular index', () => {
expect(
test.values().map((v) =>
v.toString()
)
).toEqual(['bazzing', '69']);
new (
Struct.with({
txt: Text,
u32: U32
})
)({ txt: 'foo', u32: 1234 })
.getAtIndex(1)
.toString()
).toEqual('1234');
});
});
+59 -59
View File
@@ -2,10 +2,10 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { hexToU8a, isHex, isObject, isU8a, u8aConcat } from '@polkadot/util';
import { hexToU8a, isHex, isObject, isU8a, u8aConcat, u8aToHex } from '@polkadot/util';
import Base from './Base';
import { Constructor } from '../types';
import { Codec, Constructor, ConstructorDef } from '../types';
// A Struct defines an Object with key/values - where the values are Base<T> values. It removes
// a lot of repetition from the actual coding, define a structure type, pass it the key/Base<T>
@@ -14,34 +14,57 @@ import { Constructor } from '../types';
// it needs to decoded in the specific defined order.
export default class Struct<
// The actual Class structure, i.e. key -> Class
S = { [index: string]: Constructor<Base> },
S extends ConstructorDef = ConstructorDef,
// internal type, instance of classes mapped by key
T = { [K in keyof S]: Base },
T extends { [K in keyof S]: Base } = { [K in keyof S]: Base },
// input values, mapped by key can be anything (construction)
V = { [K in keyof S]: any },
V extends { [K in keyof S]: any } = { [K in keyof S]: any },
// type names, mapped by key, name of Class in S
E = { [K in keyof S]: string }
> extends Base<T> {
E extends { [K in keyof S]: string } = { [K in keyof S]: string }
> extends Map<keyof S, Base> implements Codec {
public raw: Map<keyof S, Base>; // FIXME Remove this once we convert all types out of Base
protected _jsonMap: Map<keyof S, string>;
protected _Types: E;
constructor (Types: S, value: V | Array<any> = {} as V, jsonMap: Map<keyof S, string> = new Map()) {
const decoded = Struct.decodeStruct<S, V, T>(Types, value, jsonMap);
super(
Struct.decodeStruct(Types, value, jsonMap)
Object.entries(decoded)
);
this._jsonMap = jsonMap;
this._Types = Object
.keys(Types)
this._Types = (Object
.keys(Types) as Array<keyof S>)
.reduce((result: E, key) => {
// @ts-ignore Same as above, can't do a simple one, I'm missing something simple
result[key] = Types[key].name;
return result;
}, {} as E);
// FIXME Remove this once we convert all types out of Base
this.raw = this;
}
static decodeStruct<S, V, T> (Types: S, value: any, jsonMap: Map<keyof S, string>): T {
/**
* Decode input to pass into constructor.
*
* @param Types - Types definition.
* @param value - Value to decode, one of:
* - null
* - undefined
* - hex
* - Uint8Array
* - object with `{ key1: value1, key2: value2 }`, assuming `key1` and `key2`
* are also keys in `Types`
* - array with `[value1, value2]` assuming the array has the same length as
* `Object.keys(Types)`
* @param jsonMap
*/
private static decodeStruct<
S extends ConstructorDef,
_,
T extends { [K in keyof S]: Base }
> (Types: S, value: any, jsonMap: Map<keyof S, string>): T {
// l.debug(() => ['Struct.decode', { Types, value }]);
if (isHex(value)) {
@@ -54,15 +77,14 @@ export default class Struct<
// used to track at which index we are currently parsing in that array.
let currentIndex = 0;
return Object
.keys(Types)
return (Object
.keys(Types) as Array<keyof S>)
.reduce((raw: T, key, index) => {
// The key in the JSON can be snake_case (or other cases), but in our
// Types, result or any other maps, it's camelCase
const jsonKey = (jsonMap.get(key as any) && !value[key]) ? jsonMap.get(key as any) : key;
if (isU8a(value)) {
// @ts-ignore FIXME See below
raw[key] = new Types[key](
value.subarray(
currentIndex
@@ -70,30 +92,17 @@ export default class Struct<
);
// Move the currentIndex forward
// @ts-ignore FIXME See below
currentIndex += raw[key].encodedLength;
// @ts-ignore FIXME See below
} else if (value[jsonKey] instanceof Types[key]) {
// @ts-ignore FIXME See below
raw[key] = value[jsonKey];
} else if (Array.isArray(value) && value.length === Object.keys(Types).length) {
// @ts-ignore FIXME See below
raw[key] = new Types[key](
// @ts-ignore FIXME
value[index]
);
} else if (Array.isArray(value)) {
raw[key] = value[index] instanceof Types[key]
? value[index]
: new Types[key](value[index]);
} else if (isObject(value)) {
// @ts-ignore FIXME Ok, something weird is going on here or I just don't get it...
// it works, so ignore the checker, although it drives me batty. (It started when
// the [key in keyof T] was added, the idea is to provide better checks, which
// does backfire here, but works externally.)
raw[key] = new Types[key](
// @ts-ignore FIXME
value[jsonKey]
);
raw[key] = value[jsonKey as string] instanceof Types[key]
? value[jsonKey as string]
: new Types[key](value[jsonKey as string]);
} else {
// @ts-ignore FIXME
throw new Error(`Struct: cannot decode type "${Types[key].name}" with value "${value}".`);
throw new Error(`Struct: cannot decode type "${Types[key].name}" with value "${JSON.stringify(value)}".`);
}
return raw;
@@ -101,7 +110,7 @@ export default class Struct<
}
static with<
S = { [index: string]: Constructor<Base> }
S extends ConstructorDef
> (Types: S): Constructor<Struct<S>> {
return class extends Struct<S> {
constructor (value?: any, jsonMap?: Map<keyof S, string>) {
@@ -115,48 +124,39 @@ export default class Struct<
}
get encodedLength (): number {
return Object.values(this.raw).reduce((length, entry) => {
return [...this.values()].reduce((length, entry) => {
return length += entry.encodedLength;
}, 0);
}
getAtIndex (index: number): Base {
return this.values()[index];
return [...this.values()][index];
}
toHex () {
return u8aToHex(this.toU8a());
}
toJSON (): any {
return Object.keys(this.raw).reduce((json, key) => {
const jsonKey = this._jsonMap.get(key as any) || key;
return [...this.keys()].reduce((json, key) => {
const jsonKey = this._jsonMap.get(key) || key;
// @ts-ignore as above...
json[jsonKey] = this.raw[key].toJSON();
const value = this.get(key);
json[jsonKey] = value && value.toJSON();
return json;
}, {} as any);
}
keys (): Array<string> {
return Object.keys(this.raw);
toString () {
return JSON.stringify(this.toJSON());
}
toU8a (isBare?: boolean): Uint8Array {
return u8aConcat(
...Object.values(this.raw).map((entry) =>
...[...this.values()].map((entry) =>
entry.toU8a(isBare)
)
);
}
toString (): string {
const data = Object.keys(this.raw).map((key) =>
// @ts-ignore as above...
`${key}: ${this.raw[key].toString()}`
).join(', ');
return `{${data}}`;
}
values (): Array<Base> {
return Object.values(this.raw);
}
}
+6 -26
View File
@@ -2,7 +2,7 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { AnyU8a, Constructor } from '../types';
import { Constructor, ConstructorDef } from '../types';
import Base from './Base';
import Struct from './Struct';
@@ -11,32 +11,12 @@ import Struct from './Struct';
// while the U8a encoding is handled in the same way as a Struct
export default class Tuple<
// S & T definitions maps to what we have in Struct (naming documented there)
S = { [index: string]: Constructor<Base> },
T = { [K in keyof S]: Base },
V = { [K in keyof S]: any }
S extends ConstructorDef = { [index: string]: Constructor<Base> },
T extends { [K in keyof S]: Base }= { [K in keyof S]: Base },
V extends { [K in keyof S]: any } = { [K in keyof S]: any }
> extends Struct<S, T, V> {
constructor (Types: S, value?: V | AnyU8a, jsonMap?: Map<keyof S, string>) {
super(Types, Tuple.decodeTuple(Types, value), jsonMap);
}
static decodeTuple<S, V> (Types: S, value: V | AnyU8a): V {
// If the input is an array, we convert it to a map
if (Array.isArray(value)) {
return Object
.keys(Types)
.reduce((result, key, index) => {
// @ts-ignore FIXME these types are a headache
result[key] = value[index];
return result;
}, {} as V);
}
// Or else, just decode like a normal Struct
return value as V;
}
static with<
S = { [index: string]: Constructor<Base> }
S extends ConstructorDef= { [index: string]: Constructor<Base> }
> (Types: S): Constructor<Tuple<S>> {
return class extends Tuple<S> {
constructor (value?: any) {
@@ -46,7 +26,7 @@ export default class Tuple<
}
toJSON (): any {
return Object.values(this.raw).map((entry) =>
return [...this.values()].map((entry) =>
entry.toJSON()
);
}
+1 -2
View File
@@ -4,13 +4,12 @@
import { assert } from '@polkadot/util';
import { Constructor } from '../types';
import Text from '../Text';
import Base from './Base';
import Tuple from './Tuple';
import Vector from './Vector';
type Constructor = { new (value?: any): Base };
export enum TypeDefInfo {
Plain,
Tuple,
+16 -1
View File
@@ -5,12 +5,27 @@
import BN from 'bn.js';
import * as Classes from './index';
import Base from './codec/Base';
import { U8a, UInt } from './codec';
export type AnyNumber = UInt | BN | Uint8Array | number | string;
export type AnyU8a = U8a | Uint8Array | Array<number> | string;
export interface Codec {
encodedLength: number;
toHex(): string
toJSON(): any;
toString(): string;
toU8a(isBare?: boolean): Uint8Array;
}
export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a'
export type CodecTypes = keyof typeof Classes;
export type Constructor<T> = { new(value?: any): T };
export type Constructor<T = Base> = { new(value?: any): T };
export type ConstructorDef<T = Base> = { [index: string]: Constructor<T> };
export type TypeDef<T = Base> = { [index: string]: Base<T> };