Text extends String (#399)
* Text extends String * Fix type * Cleaner code * Add explicit return type
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { CodecTo } from './types';
|
||||||
import Text from './Text';
|
import Text from './Text';
|
||||||
import U8a from './codec/U8a';
|
import U8a from './codec/U8a';
|
||||||
|
|
||||||
@@ -20,12 +21,12 @@ describe('Text', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('encode', () => {
|
describe('encode', () => {
|
||||||
const testEncode = (to: 'toString' | 'toU8a', expected: string | Uint8Array) =>
|
const testEncode = (to: CodecTo, expected: string | Uint8Array) =>
|
||||||
it(`can encode ${to}`, () => {
|
it(`can encode ${to}`, () => {
|
||||||
expect(new Text('foo')[to]()).toEqual(expected);
|
expect(new Text('foo')[to]()).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
// testEncode('toHex', '0x0c666f6f'); // FIXME Add this
|
testEncode('toHex', '0x0c666f6f');
|
||||||
testEncode('toString', 'foo');
|
testEncode('toString', 'foo');
|
||||||
testEncode('toU8a', Uint8Array.from([12, 102, 111, 111]));
|
testEncode('toU8a', Uint8Array.from([12, 102, 111, 111]));
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-17
@@ -2,10 +2,9 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { isFunction, isString, stringToU8a, u8aToString, u8aConcat } from '@polkadot/util';
|
import { isFunction, isString, stringToU8a, u8aToString, u8aConcat, u8aToHex } from '@polkadot/util';
|
||||||
|
|
||||||
import { AnyU8a } from '@polkadot/types/types';
|
import { AnyU8a, Codec } from './types';
|
||||||
import Base from './codec/Base';
|
|
||||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||||
|
|
||||||
// This is a string wrapper, along with the length. It is used both for strings as well
|
// This is a string wrapper, along with the length. It is used both for strings as well
|
||||||
@@ -16,18 +15,19 @@ import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
|||||||
// - Potentially we want a "TypeString" extension to this. Basically something that
|
// - Potentially we want a "TypeString" extension to this. Basically something that
|
||||||
// wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString"
|
// wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString"
|
||||||
// we can nicely strip types down like "T::AcountId" -> "AccountId"
|
// we can nicely strip types down like "T::AcountId" -> "AccountId"
|
||||||
export default class Text extends Base<string> {
|
export default class Text extends String implements Codec {
|
||||||
|
public raw: String; // FIXME Remove this once we convert all types out of Base
|
||||||
constructor (value: Text | string | AnyU8a | { toString: () => string } = '') {
|
constructor (value: Text | string | AnyU8a | { toString: () => string } = '') {
|
||||||
super(
|
super(
|
||||||
Text.decodeText(value)
|
Text.decodeText(value)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.raw = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string {
|
private static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string {
|
||||||
if (isString(value)) {
|
if (isString(value)) {
|
||||||
return value;
|
return value.toString();
|
||||||
} else if (value instanceof Text) {
|
|
||||||
return value.raw;
|
|
||||||
} else if (value instanceof Uint8Array) {
|
} else if (value instanceof Uint8Array) {
|
||||||
const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
|
const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
|
||||||
|
|
||||||
@@ -39,24 +39,20 @@ export default class Text extends Base<string> {
|
|||||||
return `${value}`;
|
return `${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get length (): number {
|
|
||||||
return this.raw.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
get encodedLength (): number {
|
get encodedLength (): number {
|
||||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toHex (): string {
|
||||||
|
return u8aToHex(this.toU8a());
|
||||||
|
}
|
||||||
|
|
||||||
toJSON (): any {
|
toJSON (): any {
|
||||||
return this.toString();
|
return this.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
toString (): string {
|
|
||||||
return this.raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
toU8a (isBare?: boolean): Uint8Array {
|
toU8a (isBare?: boolean): Uint8Array {
|
||||||
const encoded = stringToU8a(this.raw);
|
const encoded = stringToU8a(this.toString());
|
||||||
|
|
||||||
return isBare
|
return isBare
|
||||||
? encoded
|
? encoded
|
||||||
|
|||||||
+41
-35
@@ -2,6 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||||
import U8a from './codec/U8a';
|
import U8a from './codec/U8a';
|
||||||
import Text from './Text';
|
import Text from './Text';
|
||||||
|
|
||||||
@@ -13,30 +14,24 @@ const ALLOWED_BOXES = ['Compact', 'Option', 'Vec'];
|
|||||||
// what string provides us, however we also "tweak" the types received from the runtime, i.e.
|
// what string provides us, however we also "tweak" the types received from the runtime, i.e.
|
||||||
// we remove the `T::` prefixes found in some types for consistency accross implementation.
|
// we remove the `T::` prefixes found in some types for consistency accross implementation.
|
||||||
export default class Type extends Text {
|
export default class Type extends Text {
|
||||||
private _originalLength: number = 0;
|
private _originalLength: number;
|
||||||
|
|
||||||
constructor (value?: Text | U8a | Uint8Array | string) {
|
constructor (value: Text | U8a | Uint8Array | string = '') {
|
||||||
super(value);
|
// First decode it with Text
|
||||||
|
const textValue = new Text(value).toString();
|
||||||
|
// Then cleanup the textValue to get the @polkadot/types type, and pass the
|
||||||
|
// sanitized value to constructor
|
||||||
|
super(
|
||||||
|
Type.decodeType(
|
||||||
|
textValue
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this._originalLength = textValue.length;
|
||||||
|
|
||||||
this._cleanupTypes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE Length is used in the decoding calculations, so return the original (pre-cleanup)
|
private static decodeType (value: string): string {
|
||||||
// length of the data. Since toU8a is disabled, this does not affect encoding, but rather
|
|
||||||
// only the decoding leg, allowing the decoders to work with original pointers
|
|
||||||
get length (): number {
|
|
||||||
return this._originalLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note Since we are mangling what we get in beyond recognition, we really should
|
|
||||||
// not allow the re-encoding. Additionally, this is probably more of a decoder-only
|
|
||||||
// helper, so treat it as such.
|
|
||||||
toU8a (isBare?: boolean): Uint8Array {
|
|
||||||
throw new Error('Type::toU8a: unimplemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK(ery) Take the types and tweak them (slightly?) for consistency
|
|
||||||
private _cleanupTypes (): Type {
|
|
||||||
const mappings: Array<Mapper> = [
|
const mappings: Array<Mapper> = [
|
||||||
// <T::Balance as HasCompact>
|
// <T::Balance as HasCompact>
|
||||||
this._cleanupCompact(),
|
this._cleanupCompact(),
|
||||||
@@ -58,16 +53,27 @@ export default class Type extends Text {
|
|||||||
// `PropIndex` -> `ProposalIndex` (implementation looks the same, however meant as diff)
|
// `PropIndex` -> `ProposalIndex` (implementation looks the same, however meant as diff)
|
||||||
];
|
];
|
||||||
|
|
||||||
this._originalLength = this.raw.length;
|
return mappings.reduce((result, fn) => {
|
||||||
this.raw = mappings.reduce((result, fn) => {
|
|
||||||
return fn(result);
|
return fn(result);
|
||||||
}, this.raw).trim();
|
}, value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
// NOTE Length is used in the decoding calculations, so return the original (pre-cleanup)
|
||||||
|
// length of the data. Since toU8a is disabled, this does not affect encoding, but rather
|
||||||
|
// only the decoding leg, allowing the decoders to work with original pointers
|
||||||
|
get encodedLength (): number {
|
||||||
|
return this._originalLength + Compact.encodeU8a(this._originalLength, DEFAULT_LENGTH_BITS).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note Since we are mangling what we get in beyond recognition, we really should
|
||||||
|
// not allow the re-encoding. Additionally, this is probably more of a decoder-only
|
||||||
|
// helper, so treat it as such.
|
||||||
|
toU8a (isBare?: boolean): Uint8Array {
|
||||||
|
throw new Error('Type::toU8a: unimplemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a starting index, find the closing >
|
// given a starting index, find the closing >
|
||||||
private _findClosing (value: string, start: number): number {
|
private static _findClosing (value: string, start: number): number {
|
||||||
let depth = 0;
|
let depth = 0;
|
||||||
|
|
||||||
for (let index = start; index < value.length; index++) {
|
for (let index = start; index < value.length; index++) {
|
||||||
@@ -85,7 +91,7 @@ export default class Type extends Text {
|
|||||||
throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`);
|
throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _alias (src: string, dest: string): Mapper {
|
private static _alias (src: string, dest: string): Mapper {
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
return value.replace(
|
return value.replace(
|
||||||
new RegExp(src, 'g'), dest
|
new RegExp(src, 'g'), dest
|
||||||
@@ -93,14 +99,14 @@ export default class Type extends Text {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cleanupCompact (): Mapper {
|
private static _cleanupCompact (): Mapper {
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
for (let index = 0; index < value.length; index++) {
|
for (let index = 0; index < value.length; index++) {
|
||||||
if (value[index] !== '<') {
|
if (value[index] !== '<') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const end = this._findClosing(value, index + 1) - 14;
|
const end = Type._findClosing(value, index + 1) - 14;
|
||||||
|
|
||||||
if (value.substr(end, 14) === ' as HasCompact') {
|
if (value.substr(end, 14) === ' as HasCompact') {
|
||||||
value = `Compact<${value.substr(index + 1, end - index - 1)}>`;
|
value = `Compact<${value.substr(index + 1, end - index - 1)}>`;
|
||||||
@@ -111,7 +117,7 @@ export default class Type extends Text {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeGenerics (): Mapper {
|
private static _removeGenerics (): Mapper {
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
for (let index = 0; index < value.length; index++) {
|
for (let index = 0; index < value.length; index++) {
|
||||||
if (value[index] === '<') {
|
if (value[index] === '<') {
|
||||||
@@ -124,7 +130,7 @@ export default class Type extends Text {
|
|||||||
|
|
||||||
// we have not found anything, unwrap generic innards
|
// we have not found anything, unwrap generic innards
|
||||||
if (!box) {
|
if (!box) {
|
||||||
const end = this._findClosing(value, index + 1);
|
const end = Type._findClosing(value, index + 1);
|
||||||
|
|
||||||
value = `${value.substr(0, index)}${value.substr(end + 1)}`;
|
value = `${value.substr(0, index)}${value.substr(end + 1)}`;
|
||||||
}
|
}
|
||||||
@@ -136,12 +142,12 @@ export default class Type extends Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the PairOf wrappers
|
// remove the PairOf wrappers
|
||||||
private _removePairOf (): Mapper {
|
private static _removePairOf (): Mapper {
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
for (let index = 0; index < value.length; index++) {
|
for (let index = 0; index < value.length; index++) {
|
||||||
if (value.substr(index, 7) === 'PairOf<') {
|
if (value.substr(index, 7) === 'PairOf<') {
|
||||||
const start = index + 7;
|
const start = index + 7;
|
||||||
const end = this._findClosing(value, start);
|
const end = Type._findClosing(value, start);
|
||||||
const type = value.substr(start, end - start);
|
const type = value.substr(start, end - start);
|
||||||
|
|
||||||
value = `${value.substr(0, index)}(${type}, ${type})${value.substr(end + 1)}`;
|
value = `${value.substr(0, index)}(${type}, ${type})${value.substr(end + 1)}`;
|
||||||
@@ -153,7 +159,7 @@ export default class Type extends Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove the type traits
|
// remove the type traits
|
||||||
private _removeTraits (): Mapper {
|
private static _removeTraits (): Mapper {
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
return value
|
return value
|
||||||
// anything `T::<type>` to end up as `<type>`
|
// anything `T::<type>` to end up as `<type>`
|
||||||
@@ -168,7 +174,7 @@ export default class Type extends Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove wrapping values, i.e. Box<Proposal> -> Proposal
|
// remove wrapping values, i.e. Box<Proposal> -> Proposal
|
||||||
private _removeWrap (_check: string): Mapper {
|
private static _removeWrap (_check: string): Mapper {
|
||||||
const check = `${_check}<`;
|
const check = `${_check}<`;
|
||||||
|
|
||||||
return (value: string): string => {
|
return (value: string): string => {
|
||||||
@@ -179,7 +185,7 @@ export default class Type extends Text {
|
|||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const start = index + check.length;
|
const start = index + check.length;
|
||||||
const end = this._findClosing(value, start);
|
const end = Type._findClosing(value, start);
|
||||||
|
|
||||||
value = `${value.substr(0, index)}${value.substr(start, end - start)}${value.substr(end + 1)}`;
|
value = `${value.substr(0, index)}${value.substr(start, end - start)}${value.substr(end + 1)}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user