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
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { CodecTo } from './types';
|
||||
import Text from './Text';
|
||||
import U8a from './codec/U8a';
|
||||
|
||||
@@ -20,12 +21,12 @@ describe('Text', () => {
|
||||
});
|
||||
|
||||
describe('encode', () => {
|
||||
const testEncode = (to: 'toString' | 'toU8a', expected: string | Uint8Array) =>
|
||||
const testEncode = (to: CodecTo, expected: string | Uint8Array) =>
|
||||
it(`can encode ${to}`, () => {
|
||||
expect(new Text('foo')[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
// testEncode('toHex', '0x0c666f6f'); // FIXME Add this
|
||||
testEncode('toHex', '0x0c666f6f');
|
||||
testEncode('toString', 'foo');
|
||||
testEncode('toU8a', Uint8Array.from([12, 102, 111, 111]));
|
||||
});
|
||||
|
||||
+13
-17
@@ -2,10 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// 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 Base from './codec/Base';
|
||||
import { AnyU8a, Codec } from './types';
|
||||
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
|
||||
@@ -16,18 +15,19 @@ import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
// - Potentially we want a "TypeString" extension to this. Basically something that
|
||||
// wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString"
|
||||
// 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 } = '') {
|
||||
super(
|
||||
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)) {
|
||||
return value;
|
||||
} else if (value instanceof Text) {
|
||||
return value.raw;
|
||||
return value.toString();
|
||||
} else if (value instanceof Uint8Array) {
|
||||
const [offset, length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
|
||||
|
||||
@@ -39,24 +39,20 @@ export default class Text extends Base<string> {
|
||||
return `${value}`;
|
||||
}
|
||||
|
||||
get length (): number {
|
||||
return this.raw.length;
|
||||
}
|
||||
|
||||
get encodedLength (): number {
|
||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
}
|
||||
|
||||
toJSON (): any {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
toString (): string {
|
||||
return this.raw;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
const encoded = stringToU8a(this.raw);
|
||||
const encoded = stringToU8a(this.toString());
|
||||
|
||||
return isBare
|
||||
? encoded
|
||||
|
||||
+41
-35
@@ -2,6 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
import U8a from './codec/U8a';
|
||||
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.
|
||||
// we remove the `T::` prefixes found in some types for consistency accross implementation.
|
||||
export default class Type extends Text {
|
||||
private _originalLength: number = 0;
|
||||
private _originalLength: number;
|
||||
|
||||
constructor (value?: Text | U8a | Uint8Array | string) {
|
||||
super(value);
|
||||
constructor (value: Text | U8a | Uint8Array | string = '') {
|
||||
// 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)
|
||||
// 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 {
|
||||
private static decodeType (value: string): string {
|
||||
const mappings: Array<Mapper> = [
|
||||
// <T::Balance as HasCompact>
|
||||
this._cleanupCompact(),
|
||||
@@ -58,16 +53,27 @@ export default class Type extends Text {
|
||||
// `PropIndex` -> `ProposalIndex` (implementation looks the same, however meant as diff)
|
||||
];
|
||||
|
||||
this._originalLength = this.raw.length;
|
||||
this.raw = mappings.reduce((result, fn) => {
|
||||
return mappings.reduce((result, fn) => {
|
||||
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 >
|
||||
private _findClosing (value: string, start: number): number {
|
||||
private static _findClosing (value: string, start: number): number {
|
||||
let depth = 0;
|
||||
|
||||
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})`);
|
||||
}
|
||||
|
||||
private _alias (src: string, dest: string): Mapper {
|
||||
private static _alias (src: string, dest: string): Mapper {
|
||||
return (value: string): string => {
|
||||
return value.replace(
|
||||
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 => {
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
if (value[index] !== '<') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const end = this._findClosing(value, index + 1) - 14;
|
||||
const end = Type._findClosing(value, index + 1) - 14;
|
||||
|
||||
if (value.substr(end, 14) === ' as HasCompact') {
|
||||
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 => {
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
if (value[index] === '<') {
|
||||
@@ -124,7 +130,7 @@ export default class Type extends Text {
|
||||
|
||||
// we have not found anything, unwrap generic innards
|
||||
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)}`;
|
||||
}
|
||||
@@ -136,12 +142,12 @@ export default class Type extends Text {
|
||||
}
|
||||
|
||||
// remove the PairOf wrappers
|
||||
private _removePairOf (): Mapper {
|
||||
private static _removePairOf (): Mapper {
|
||||
return (value: string): string => {
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
if (value.substr(index, 7) === 'PairOf<') {
|
||||
const start = index + 7;
|
||||
const end = this._findClosing(value, start);
|
||||
const end = Type._findClosing(value, start);
|
||||
const type = value.substr(start, end - start);
|
||||
|
||||
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
|
||||
private _removeTraits (): Mapper {
|
||||
private static _removeTraits (): Mapper {
|
||||
return (value: string): string => {
|
||||
return value
|
||||
// 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
|
||||
private _removeWrap (_check: string): Mapper {
|
||||
private static _removeWrap (_check: string): Mapper {
|
||||
const check = `${_check}<`;
|
||||
|
||||
return (value: string): string => {
|
||||
@@ -179,7 +185,7 @@ export default class Type extends Text {
|
||||
|
||||
if (index !== -1) {
|
||||
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)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user