Add Result type (#1451)
* Add Result class * Allow createType to operate on Result * Formatting of Result type in interfaces (incl. use) * Tests for Result * Linting & createType seems to work, any final round of gaps? * Bump metadata containing Result<Null,DispatchError> * extractTypes handles Result (validation) * Remove any from creation params * Ok, give up trying to make `with` sane... it stays enum-like :( * Move Enum protected decode member to file-level
This commit is contained in:
@@ -24,13 +24,14 @@ function extractTypes (types: string[]): any[] {
|
||||
case TypeDefInfo.VecFixed:
|
||||
return extractTypes([(decoded.ext as TypeDefExtVecFixed).type]);
|
||||
|
||||
case TypeDefInfo.Result:
|
||||
case TypeDefInfo.Tuple:
|
||||
return extractTypes(
|
||||
(decoded.sub as TypeDef[]).map((sub): string => sub.type)
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Error(`Uhandled: Unnable to create and validate type from ${type}`);
|
||||
throw new Error(`Unhandled: Unable to create and validate type from ${type}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
{
|
||||
"name": "BatchExecuted",
|
||||
"args": [
|
||||
"Vec<DispatchResult>"
|
||||
"Vec<Result<Null,DispatchError>>"
|
||||
],
|
||||
"documentation": []
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -44,6 +44,53 @@ function extractDef (_def: Record<string, InterfaceTypes | Constructor> | string
|
||||
};
|
||||
}
|
||||
|
||||
function createFromValue (def: TypesDef, index = 0, value?: any): Decoded {
|
||||
const Clazz = Object.values(def)[index];
|
||||
|
||||
assert(!isUndefined(Clazz), `Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`);
|
||||
|
||||
return {
|
||||
index,
|
||||
value: new Clazz(value)
|
||||
};
|
||||
}
|
||||
|
||||
function decodeFromJSON (def: TypesDef, key: string, value?: any): Decoded {
|
||||
// JSON comes in the form of { "<type (lowercased)>": "<value for type>" }, here we
|
||||
// additionally force to lower to ensure forward compat
|
||||
const keys = Object.keys(def).map((k): string => k.toLowerCase());
|
||||
const keyLower = key.toLowerCase();
|
||||
const index = keys.indexOf(keyLower);
|
||||
|
||||
assert(index !== -1, `Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`);
|
||||
|
||||
return createFromValue(def, index, value);
|
||||
}
|
||||
|
||||
function decodeFromString (def: TypesDef, value: string): Decoded {
|
||||
return isHex(value)
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
? decodeFromValue(def, hexToU8a(value))
|
||||
: decodeFromJSON(def, value);
|
||||
}
|
||||
|
||||
function decodeFromValue (def: TypesDef, value?: any): Decoded {
|
||||
if (isU8a(value)) {
|
||||
return createFromValue(def, value[0], value.subarray(1));
|
||||
} else if (isNumber(value)) {
|
||||
return createFromValue(def, value);
|
||||
} else if (isString(value)) {
|
||||
return decodeFromString(def, value.toString());
|
||||
} else if (isObject(value)) {
|
||||
const key = Object.keys(value)[0];
|
||||
|
||||
return decodeFromJSON(def, key, value[key]);
|
||||
}
|
||||
|
||||
// Worst-case scenario, return the first with default
|
||||
return createFromValue(def, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Enum
|
||||
* @description
|
||||
@@ -78,59 +125,13 @@ export default class Enum extends Base<Codec> {
|
||||
// NOTE We check the index path first, before looking at values - this allows treating
|
||||
// the optional indexes before anything else, more-specific > less-specific
|
||||
if (isNumber(index)) {
|
||||
return Enum.createValue(def, index, value);
|
||||
return createFromValue(def, index, value);
|
||||
} else if (value instanceof Enum) {
|
||||
return Enum.createValue(def, value._index, value.raw);
|
||||
return createFromValue(def, value._index, value.raw);
|
||||
}
|
||||
|
||||
// Or else, we just look at `value`
|
||||
return Enum.decodeViaValue(def, value);
|
||||
}
|
||||
|
||||
private static decodeViaValue (def: TypesDef, value?: any): Decoded {
|
||||
if (isU8a(value)) {
|
||||
return Enum.createValue(def, value[0], value.subarray(1));
|
||||
} else if (isNumber(value)) {
|
||||
return Enum.createValue(def, value);
|
||||
} else if (isString(value)) {
|
||||
return Enum.decodeViaString(def, value.toString());
|
||||
} else if (isObject(value)) {
|
||||
const key = Object.keys(value)[0];
|
||||
|
||||
return Enum.createViaJSON(def, key, value[key]);
|
||||
}
|
||||
|
||||
// Worst-case scenario, return the first with default
|
||||
return Enum.createValue(def, 0);
|
||||
}
|
||||
|
||||
private static decodeViaString (def: TypesDef, value: string): Decoded {
|
||||
return isHex(value)
|
||||
? Enum.decodeViaValue(def, hexToU8a(value))
|
||||
: Enum.createViaJSON(def, value);
|
||||
}
|
||||
|
||||
private static createViaJSON (def: TypesDef, key: string, value?: any): Decoded {
|
||||
// JSON comes in the form of { "<type (lowercased)>": "<value for type>" }, here we
|
||||
// additionally force to lower to ensure forward compat
|
||||
const keys = Object.keys(def).map((k): string => k.toLowerCase());
|
||||
const keyLower = key.toLowerCase();
|
||||
const index = keys.indexOf(keyLower);
|
||||
|
||||
assert(index !== -1, `Cannot map Enum JSON, unable to find '${key}' in ${keys.join(', ')}`);
|
||||
|
||||
return Enum.createValue(def, index, value);
|
||||
}
|
||||
|
||||
private static createValue (def: TypesDef, index = 0, value?: any): Decoded {
|
||||
const Clazz = Object.values(def)[index];
|
||||
|
||||
assert(!isUndefined(Clazz), `Unable to create Enum via index ${index}, in ${Object.keys(def).join(', ')}`);
|
||||
|
||||
return {
|
||||
index,
|
||||
value: new Clazz(value)
|
||||
};
|
||||
return decodeFromValue(def, value);
|
||||
}
|
||||
|
||||
public static with (Types: Record<string, InterfaceTypes | Constructor> | string[]): EnumConstructor<Enum> {
|
||||
@@ -246,15 +247,20 @@ export default class Enum extends Base<Codec> {
|
||||
return this._index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a raw struct representation of the enum types
|
||||
*/
|
||||
protected toRawStruct (): string[] | Record<string, string> {
|
||||
return this._isBasic
|
||||
? Object.keys(this._def)
|
||||
: Struct.typesToMap(this._def);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the base runtime type name for this instance
|
||||
*/
|
||||
public toRawType (): string {
|
||||
const _enum = this._isBasic
|
||||
? Object.keys(this._def)
|
||||
: Struct.typesToMap(this._def);
|
||||
|
||||
return JSON.stringify({ _enum });
|
||||
return JSON.stringify({ _enum: this.toRawStruct() });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2017-2019 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import '../injector';
|
||||
|
||||
import { hexToString } from '@polkadot/util';
|
||||
|
||||
import { u32, Text } from '../primitive';
|
||||
import { createType } from './create';
|
||||
import Result from './Result';
|
||||
|
||||
describe('Result', (): void => {
|
||||
const Type = Result.with({ Ok: u32, Error: Text });
|
||||
|
||||
it('has a sane toRawType representation', (): void => {
|
||||
expect(new Type().toRawType()).toEqual('Result<u32,Text>');
|
||||
});
|
||||
|
||||
it('decodes from a u8a (success)', (): void => {
|
||||
const result = new Type(new Uint8Array([0, 1, 2, 3, 4]));
|
||||
|
||||
expect(result.isOk);
|
||||
expect(result.asOk.toU8a()).toEqual(new Uint8Array([1, 2, 3, 4]));
|
||||
expect(result.toHex()).toEqual('0x0001020304');
|
||||
expect(result.toJSON()).toEqual({
|
||||
Ok: 0x04030201
|
||||
});
|
||||
});
|
||||
|
||||
it('decodes from a u8a (error)', (): void => {
|
||||
const result = new Type(new Uint8Array([1, 4 << 2, 100, 101, 102, 103]));
|
||||
|
||||
expect(result.isError);
|
||||
expect(result.asError.toU8a()).toEqual(new Uint8Array([4 << 2, 100, 101, 102, 103]));
|
||||
expect(result.toHex()).toEqual('0x011064656667');
|
||||
expect(result.toJSON()).toEqual({
|
||||
Error: hexToString('0x64656667')
|
||||
});
|
||||
});
|
||||
|
||||
it('decodes from a JSON representation', (): void => {
|
||||
const result = new Type({ Error: 'error' });
|
||||
|
||||
expect(result.toHex()).toEqual('0x01146572726f72');
|
||||
});
|
||||
|
||||
it('returns a proper raw typedef rom a built-in', (): void => {
|
||||
expect(createType('DispatchResult').toRawType()).toEqual('Result<Null,Text>');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2017-2019 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Codec, Constructor, InterfaceTypes } from '../types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Enum from './Enum';
|
||||
|
||||
/**
|
||||
* @name Result
|
||||
* @description
|
||||
* A Result maps to the Rust Result type, that can either wrap a success or error value
|
||||
*/
|
||||
export default class Result<O extends Codec, E extends Codec> extends Enum {
|
||||
public constructor (Ok: Constructor<O> | InterfaceTypes, Error: Constructor<E> | InterfaceTypes, value?: any) {
|
||||
// NOTE This is order-dependent, Ok (with index 0) needs to be first
|
||||
super({ Ok, Error }, value);
|
||||
}
|
||||
|
||||
public static with<O extends Codec, E extends Codec> (Types: { Ok: Constructor<O> | InterfaceTypes;Error: Constructor<E> | InterfaceTypes }): Constructor<Result<O, E>> {
|
||||
return class extends Result<O, E> {
|
||||
public constructor (value?: any) {
|
||||
super(Types.Ok, Types.Error, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the wrapper Error value (if isError)
|
||||
*/
|
||||
public get asError (): E {
|
||||
assert(this.isError, 'Cannot extract Error value from Ok result, check isError first');
|
||||
|
||||
return this.value as E;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the wrapper Ok value (if isOk)
|
||||
*/
|
||||
public get asOk (): O {
|
||||
assert(this.isOk, 'Cannot extract Ok value from Error result, check isOk first');
|
||||
|
||||
return this.value as O;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the Result has no value
|
||||
*/
|
||||
public get isEmpty (): boolean {
|
||||
return this.isOk && this.raw.isEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the Result wraps an Error value
|
||||
*/
|
||||
public get isError (): boolean {
|
||||
return !this.isOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the Result wraps an Ok value
|
||||
*/
|
||||
public get isOk (): boolean {
|
||||
return this.index === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the base runtime type name for this instance
|
||||
*/
|
||||
public toRawType (): string {
|
||||
const Types = this.toRawStruct() as { Ok: any; Error: any };
|
||||
|
||||
return `Result<${Types.Ok},${Types.Error}>`;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,12 @@ describe('createType', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
it('allows creation of a Result', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe('Result<u32,Text>', ['0x011064656667']).toJSON()
|
||||
).toEqual({ Error: 'defg' });
|
||||
});
|
||||
|
||||
it('allows creation of a Tuple', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON()
|
||||
|
||||
@@ -11,6 +11,7 @@ import Compact from '../Compact';
|
||||
import Enum from '../Enum';
|
||||
import Linkage from '../Linkage';
|
||||
import Option from '../Option';
|
||||
import Result from '../Result';
|
||||
import CodecSet from '../Set';
|
||||
import Struct from '../Struct';
|
||||
import Tuple from '../Tuple';
|
||||
@@ -72,6 +73,13 @@ const infoMapping: Record<TypeDefInfo, (value: TypeDef) => Constructor> = {
|
||||
[TypeDefInfo.Plain]: (value: TypeDef): Constructor =>
|
||||
getTypeRegistry().getOrThrow(value.type, `Unable to find plain type for ${JSON.stringify(value)}`),
|
||||
|
||||
[TypeDefInfo.Result]: (value: TypeDef): Constructor => {
|
||||
const [okDef, errorDef] = getSubDefArray(value);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
return Result.with({ Ok: getTypeClass(okDef), Error: getTypeClass(errorDef) });
|
||||
},
|
||||
|
||||
[TypeDefInfo.Set]: (value: TypeDef): Constructor => {
|
||||
const result: Record<string, number> = {};
|
||||
|
||||
|
||||
@@ -21,6 +21,63 @@ describe('getTypeDef', (): void => {
|
||||
).toThrow(/Unable to find closing matching/);
|
||||
});
|
||||
|
||||
it('maps empty tuples to Null', (): void => {
|
||||
expect(
|
||||
getTypeDef('()')
|
||||
).toEqual({
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'Null'
|
||||
});
|
||||
});
|
||||
|
||||
it('properly decodes a Result<u32, Text>', (): void => {
|
||||
expect(
|
||||
getTypeDef('Result<u32, Text>')
|
||||
).toEqual({
|
||||
info: TypeDefInfo.Result,
|
||||
type: 'Result<u32,Text>',
|
||||
sub: [
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'u32'
|
||||
},
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'Text'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('properly decodes a Result<Result<(), u32>, Text>', (): void => {
|
||||
expect(
|
||||
getTypeDef('Result<Result<Null,u32>,Text>')
|
||||
).toEqual({
|
||||
info: TypeDefInfo.Result,
|
||||
type: 'Result<Result<Null,u32>,Text>',
|
||||
sub: [
|
||||
{
|
||||
info: TypeDefInfo.Result,
|
||||
type: 'Result<Null,u32>',
|
||||
sub: [
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'Null'
|
||||
},
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'u32'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
info: TypeDefInfo.Plain,
|
||||
type: 'Text'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a type structure', (): void => {
|
||||
expect(
|
||||
getTypeDef('(u32, Compact<u32>, Vec<u64>, Option<u128>, DoubleMap<u128>, (Text,Vec<(Bool,u128)>))')
|
||||
|
||||
@@ -56,7 +56,6 @@ function _decodeStruct (value: TypeDef, type: string, subType: string): TypeDef
|
||||
return _decodeSet(value, parsed[keys[0]]);
|
||||
}
|
||||
|
||||
value.info = TypeDefInfo.Struct;
|
||||
value.sub = keys.map((name): TypeDef =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
getTypeDef(parsed[name], name)
|
||||
@@ -74,7 +73,6 @@ function _decodeFixedVec (value: TypeDef, type: string, subType: string): TypeDe
|
||||
// as a first round, only u8 via u8aFixed, we can add more support
|
||||
assert(vecLen <= 256, `${type}: Only support for [Type; <length>], where length <= 256`);
|
||||
|
||||
value.info = TypeDefInfo.VecFixed;
|
||||
value.ext = { length: vecLen, type: vecType } as unknown as TypeDefExtVecFixed;
|
||||
|
||||
return value;
|
||||
@@ -82,7 +80,6 @@ function _decodeFixedVec (value: TypeDef, type: string, subType: string): TypeDe
|
||||
|
||||
// decode a tuple
|
||||
function _decodeTuple (value: TypeDef, type: string, subType: string): TypeDef {
|
||||
value.info = TypeDefInfo.Tuple;
|
||||
value.sub = typeSplit(subType).map((inner): TypeDef =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
getTypeDef(inner)
|
||||
@@ -91,7 +88,7 @@ function _decodeTuple (value: TypeDef, type: string, subType: string): TypeDef {
|
||||
return value;
|
||||
}
|
||||
|
||||
function hasWrapper (type: string, [start, end]: [string, string, any]): boolean {
|
||||
function hasWrapper (type: string, [start, end]: [string, string, TypeDefInfo, any?]): boolean {
|
||||
if (type.substr(0, start.length) !== start) {
|
||||
return false;
|
||||
}
|
||||
@@ -101,21 +98,23 @@ function hasWrapper (type: string, [start, end]: [string, string, any]): boolean
|
||||
return true;
|
||||
}
|
||||
|
||||
const nestedExtraction: [string, string, (value: TypeDef, type: string, subType: string) => TypeDef][] = [
|
||||
['[', ']', _decodeFixedVec],
|
||||
['{', '}', _decodeStruct],
|
||||
['(', ')', _decodeTuple]
|
||||
const nestedExtraction: [string, string, TypeDefInfo, (value: TypeDef, type: string, subType: string) => TypeDef][] = [
|
||||
['[', ']', TypeDefInfo.VecFixed, _decodeFixedVec],
|
||||
['{', '}', TypeDefInfo.Struct, _decodeStruct],
|
||||
['(', ')', TypeDefInfo.Tuple, _decodeTuple],
|
||||
// the inner for result is the same as tuple
|
||||
['Result<', '>', TypeDefInfo.Result, _decodeTuple]
|
||||
];
|
||||
|
||||
const wrappedExtraction: [string, string, TypeDefInfo][] = [
|
||||
['Compact<', '>', TypeDefInfo.Compact],
|
||||
['Option<', '>', TypeDefInfo.Option],
|
||||
['Vec<', '>', TypeDefInfo.Vec],
|
||||
['DoubleMap<', '>', TypeDefInfo.DoubleMap],
|
||||
['Linkage<', '>', TypeDefInfo.Linkage],
|
||||
['DoubleMap<', '>', TypeDefInfo.DoubleMap]
|
||||
['Option<', '>', TypeDefInfo.Option],
|
||||
['Vec<', '>', TypeDefInfo.Vec]
|
||||
];
|
||||
|
||||
function extractSubType (type: string, [start, end]: [string, string, any]): string {
|
||||
function extractSubType (type: string, [start, end]: [string, string, TypeDefInfo, any?]): string {
|
||||
return type.substr(start.length, type.length - start.length - end.length);
|
||||
}
|
||||
|
||||
@@ -129,7 +128,9 @@ export function getTypeDef (_type: string, name?: string): TypeDef {
|
||||
);
|
||||
|
||||
if (nested) {
|
||||
return nested[2](value, type, extractSubType(type, nested));
|
||||
value.info = nested[2];
|
||||
|
||||
return nested[3](value, type, extractSubType(type, nested));
|
||||
}
|
||||
|
||||
const wrapped = wrappedExtraction.find((wrapped): boolean =>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
type Mapper = (value: string) => string;
|
||||
|
||||
const ALLOWED_BOXES = ['Compact', 'DoubleMap', 'Linkage', 'Option', 'Vec'];
|
||||
const ALLOWED_BOXES = ['Compact', 'DoubleMap', 'Linkage', 'Result', 'Option', 'Vec'];
|
||||
|
||||
const mappings: Mapper[] = [
|
||||
// alias <T::InherentOfflineReport as InherentOfflineReport>::Inherent -> InherentOfflineReport
|
||||
|
||||
@@ -18,6 +18,7 @@ export enum TypeDefInfo {
|
||||
Linkage,
|
||||
Option,
|
||||
Plain,
|
||||
Result,
|
||||
Set,
|
||||
Struct,
|
||||
Tuple,
|
||||
|
||||
@@ -12,6 +12,7 @@ export { default as Compact } from './Compact';
|
||||
// export { default as Date } from './Date';
|
||||
export { default as Enum } from './Enum';
|
||||
export { default as Option } from './Option';
|
||||
export { default as Result } from './Result';
|
||||
export { default as Set } from './Set';
|
||||
export { default as Struct } from './Struct';
|
||||
// export { default as StructAny } from './StructAny';
|
||||
|
||||
@@ -4,14 +4,8 @@
|
||||
|
||||
export default {
|
||||
types: {
|
||||
// Not 100% on this one this is defined as - result::Result<(), Error>
|
||||
// we would probably need a result codec type (instead of approximating it)
|
||||
DispatchResult: {
|
||||
_enum: {
|
||||
Ok: 'Null',
|
||||
Error: 'Text'
|
||||
}
|
||||
},
|
||||
// This is not deprecated, only there for a short while, replaced by Result<(), DispatchError>
|
||||
DispatchResult: 'Result<(), Text>',
|
||||
DispatchResultOf: 'DispatchResult'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Auto-generated via `yarn build:interfaces`, do not edit
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import { Enum } from '@polkadot/types/codec';
|
||||
import { Text } from '@polkadot/types/primitive';
|
||||
import { Result } from '@polkadot/types/codec';
|
||||
import { Null, Text } from '@polkadot/types/primitive';
|
||||
|
||||
/** Enum */
|
||||
export interface DispatchResult extends Enum {
|
||||
/** 0:: Ok */
|
||||
readonly isOk: boolean;
|
||||
/** 1:: Error(Text) */
|
||||
/** Result<Null, Text> */
|
||||
export interface DispatchResult extends Result<Null, Text> {
|
||||
/** Error:: (Text) */
|
||||
readonly isError: boolean;
|
||||
/** Text */
|
||||
readonly asError: Text;
|
||||
/** Ok:: */
|
||||
readonly isOk: boolean;
|
||||
}
|
||||
|
||||
/** DispatchResult */
|
||||
|
||||
@@ -9,13 +9,7 @@ import { isString, stringCamelCase, stringUpperFirst } from '@polkadot/util';
|
||||
|
||||
import { getTypeDef } from '../../codec/create';
|
||||
import * as definitions from '../../interfaces/definitions';
|
||||
import {
|
||||
createImportCode, createImports,
|
||||
exportInterface, exportType,
|
||||
formatCompact, formatOption, formatTuple, formatVec,
|
||||
FOOTER, HEADER,
|
||||
setImports, TypeImports
|
||||
} from '../util';
|
||||
import { createImportCode, createImports, exportInterface, exportType, formatCompact, formatOption, formatResult, formatTuple, formatVec, FOOTER, HEADER, setImports, TypeImports } from '../util';
|
||||
|
||||
interface Imports extends TypeImports {
|
||||
interfaces: [string, string][];
|
||||
@@ -91,6 +85,34 @@ function tsPlain ({ name: plainName, type }: TypeDef, imports: TypeImports): str
|
||||
return exportType(plainName, type);
|
||||
}
|
||||
|
||||
function tsResultGetter (resultName = '', getter: 'Ok' | 'Error', { info, name = '', type }: TypeDef, imports: TypeImports): string {
|
||||
const [resultType, asGetter] = type === 'Null'
|
||||
? ['', '']
|
||||
: [`(${type})`, createGetter(`as${getter}`, type, imports)];
|
||||
const isGetter = createGetter(`is${getter}`, 'boolean', imports, `${getter}:: ${name}${resultType}`);
|
||||
|
||||
switch (info) {
|
||||
case TypeDefInfo.Plain:
|
||||
case TypeDefInfo.Vec:
|
||||
return `${isGetter}${asGetter}`;
|
||||
|
||||
default:
|
||||
throw new Error(`Result: ${resultName}: Unhandled type ${TypeDefInfo[info]}`);
|
||||
}
|
||||
}
|
||||
|
||||
function tsResult ({ name: resultName, sub, type }: TypeDef, imports: TypeImports): string {
|
||||
const [okDef, errorDef] = (sub as TypeDef[]);
|
||||
const inner = [
|
||||
tsResultGetter(resultName, 'Error', errorDef, imports),
|
||||
tsResultGetter(resultName, 'Ok', okDef, imports)
|
||||
].join('');
|
||||
|
||||
setImports(imports, [type]);
|
||||
|
||||
return exportInterface(resultName, formatResult(okDef.type, errorDef.type), inner);
|
||||
}
|
||||
|
||||
function _tsStructGetterType (structName: string | undefined, { info, sub, type }: TypeDef, imports: TypeImports): [string, string] {
|
||||
let _type;
|
||||
|
||||
@@ -203,6 +225,7 @@ function generateInterfaces ({ types }: { types: Record<string, any> }, imports:
|
||||
[TypeDefInfo.Null]: errorUnhandled,
|
||||
[TypeDefInfo.Option]: tsOption,
|
||||
[TypeDefInfo.Plain]: tsPlain,
|
||||
[TypeDefInfo.Result]: tsResult,
|
||||
[TypeDefInfo.Set]: tsSet,
|
||||
[TypeDefInfo.Struct]: tsStruct,
|
||||
[TypeDefInfo.Tuple]: tsTuple,
|
||||
|
||||
@@ -40,6 +40,13 @@ export function formatCompact (inner: string): string {
|
||||
return `Compact<${inner}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the inner `O` & `E`, return a `Result<O, E>` string
|
||||
*/
|
||||
export function formatResult (innerOk: string, innerError: string): string {
|
||||
return `Result<${innerOk}, ${innerError}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the inner `T`, return a `Option<T>` string
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user