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:
committed by
Jaco Greeff
parent
174978a072
commit
f2287f38c5
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Vendored
+16
-1
@@ -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> };
|
||||
|
||||
Reference in New Issue
Block a user