Text extends String (#399)

* Text extends String

* Fix type

* Cleaner code

* Add explicit return type
This commit is contained in:
Amaury Martiny
2018-11-15 14:19:41 +01:00
committed by GitHub
parent ca2f69d086
commit f381a4d045
3 changed files with 57 additions and 54 deletions
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)}`;
}