@cosmology/ast
Version:
Cosmos TypeScript AST generation
231 lines (199 loc) • 6.32 kB
text/typescript
import * as t from '@babel/types';
import { arrowFunctionExpression } from '../../../utils';
import { AminoParseContext } from '../../context';
import { ProtoType, ProtoField } from '@cosmology/types';
import { protoFieldsToArray } from '../utils';
import { arrayTypes, toAmino } from './utils';
import { getFieldOptionality, getOneOfs } from '../../proto';
import { shouldOmitEmpty } from '@cosmology/utils';
const needsImplementation = (name: string, field: ProtoField) => {
throw new Error(`need to implement toAmino (${field.type} rules[${field.rule}] name[${name}])`);
}
const warningDefaultImplementation = (name: string, field: ProtoField) => {
console.warn(`need to implement toAmino (${field.type} rules[${field.rule}] name[${name}])`);
}
export interface ToAminoParseField {
context: AminoParseContext;
field: ProtoField;
currentProtoPath: string;
scope: string[];
fieldPath: ProtoField[];
nested: number;
isOptional: boolean;
}
export const toAminoParseField = ({
context,
field,
currentProtoPath,
scope: previousScope,
fieldPath: previousFieldPath,
nested,
isOptional
}: ToAminoParseField) => {
const scope = [field.name, ...previousScope];
const fieldPath = [field, ...previousFieldPath];
const args = {
context,
field,
currentProtoPath,
scope,
fieldPath,
nested,
isOptional
};
// arrays
if (field.rule === 'repeated') {
switch (field.type) {
case 'string':
return toAmino.stringArray(args);
case 'int64':
case 'sint64':
case 'uint64':
case 'fixed64':
case 'sfixed64':
return toAmino.scalarArray(args, arrayTypes.long);
case 'double':
case 'float':
case 'int32':
case 'sint32':
case 'uint32':
case 'fixed32':
case 'sfixed32':
case 'bool':
case 'bytes':
return toAmino.defaultType(args);
}
switch (field.parsedType.type) {
case 'Enum':
return toAmino.defaultType(args);
case 'Type':
return toAmino.typeArray(args);
}
return needsImplementation(field.name, field);
}
// casting Any types
if (field.type === 'google.protobuf.Any') {
switch (field.options?.['(cosmos_proto.accepts_interface)']) {
case 'cosmos.crypto.PubKey':
return toAmino.pubkey(args);
}
}
// special types...
switch (field.type) {
case 'Timestamp':
case 'google.protobuf.Timestamp':
return toAmino.defaultType(args)
case 'cosmos.base.v1beta1.Coin':
return toAmino.coin(args);
// TODO check can we just
// make pieces optional and avoid hard-coding this type?
case 'ibc.core.client.v1.Height':
case 'Height':
return toAmino.height(args);
case 'Duration':
case 'google.protobuf.Duration':
return toAmino.duration(args);
default:
}
// Types/Enums
switch (field.parsedType.type) {
case 'Enum':
return toAmino.defaultType(args);
case 'Type':
return toAmino.type(args);
}
if (field.type === 'bytes') {
// bytes [RawContractMessage]
if (field.options?.['(gogoproto.casttype)'] === 'RawContractMessage') {
return toAmino.rawBytes(args);
}
// bytes [WASMByteCode]
// TODO use a better option for this in proto source
if (field.options?.['(gogoproto.customname)'] === 'WASMByteCode') {
return toAmino.wasmByteCode(args);
}
}
let omitEmpty = shouldOmitEmpty(args.context, field);
// scalar types...
switch (field.type) {
case 'string':
return toAmino.string(args, omitEmpty);
case 'int64':
case 'sint64':
case 'uint64':
case 'fixed64':
case 'sfixed64':
return toAmino.long(args, omitEmpty);
case 'double':
case 'float':
case 'int32':
case 'sint32':
case 'uint32':
case 'fixed32':
case 'sfixed32':
case 'bool':
return toAmino.defaultType(args, omitEmpty)
case 'bytes':
return toAmino.defaultType(args, false)
default:
warningDefaultImplementation(field.name, field);
return toAmino.defaultType(args)
}
};
interface toAminoJSON {
context: AminoParseContext;
proto: ProtoType;
}
export const toAminoJsonMethod = ({
context,
proto
}: toAminoJSON) => {
const toAminoParams = t.objectPattern(
protoFieldsToArray(proto).map((field) =>
t.objectProperty(
t.identifier(field.name),
t.identifier(field.name),
false,
true)
)
);
toAminoParams.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(proto.name)))
const oneOfs = getOneOfs(proto);
const fields = protoFieldsToArray(proto).map((field) => {
const isOneOf = oneOfs.includes(field.name);
const isOptional = getFieldOptionality(context, field, isOneOf);
const aminoField = toAminoParseField({
context,
field,
currentProtoPath: context.ref.filename,
scope: [],
fieldPath: [],
nested: 0,
isOptional
});
return {
ctx: context,
field: aminoField
}
});
// const ctxs = fields.map(({ ctx }) => ctx);
// ctxs.forEach(ctx => {
// // console.log('imports, ', ctx)
// })
return arrowFunctionExpression(
[
toAminoParams
],
t.blockStatement([
t.returnStatement(
t.objectExpression(
fields.map(({ field }) => field)
)
)
]),
t.tsTypeAnnotation(t.tsIndexedAccessType(
t.tsTypeReference(t.identifier(proto.name + 'AminoType')),
t.tsLiteralType(t.stringLiteral('value'))
))
);
};