@cosmology/ast
Version:
Cosmos TypeScript AST generation
743 lines (658 loc) • 20.9 kB
text/typescript
import * as t from "@babel/types";
import { fromAminoMethod } from "./index";
import { identifier, callExpression, TypeLong, BILLION } from "../../../utils";
import { getFieldsTypeName, getInterfaceFromAminoName } from "..";
import { getDefaultTSTypeFromProtoType, getFieldNames } from "../../types";
import { ProtoType } from "@cosmology/types";
import { ProtoParseContext } from "../../context";
import { camel } from "@cosmology/utils";
const setNotUndefinedAndNotNull = (
args: fromAminoMethod,
value?: t.Expression
): t.Statement => {
const { propName, origName } = getFieldNames(args.field);
return t.ifStatement(
t.logicalExpression(
"&&",
t.binaryExpression(
"!==",
t.memberExpression(t.identifier("object"), t.identifier(origName)),
t.identifier("undefined")
),
t.binaryExpression(
"!==",
t.memberExpression(t.identifier("object"), t.identifier(origName)),
t.nullLiteral()
)
),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("message"), t.identifier(propName)),
value
? value
: t.memberExpression(t.identifier("object"), t.identifier(origName))
)
),
])
);
};
export const fromAmino = {
// message.sender = object.sender ?? "";
string(args: fromAminoMethod) {
return setNotUndefinedAndNotNull(args);
},
// message.disableMacros = object.disableMacros ?? false;
bool(args: fromAminoMethod) {
return setNotUndefinedAndNotNull(args);
},
// message.doubleValue = object.doubleValue ?? 0;
number(args: fromAminoMethod) {
return setNotUndefinedAndNotNull(args);
},
int32(args: fromAminoMethod) {
return fromAmino.number(args);
},
uint32(args: fromAminoMethod) {
return fromAmino.number(args);
},
sint32(args: fromAminoMethod) {
return fromAmino.number(args);
},
fixed32(args: fromAminoMethod) {
return fromAmino.number(args);
},
sfixed32(args: fromAminoMethod) {
return fromAmino.number(args);
},
double(args: fromAminoMethod) {
return fromAmino.number(args);
},
float(args: fromAminoMethod) {
return fromAmino.number(args);
},
// if( object.myInt64Value !== undefined && object.myInt64Value !== null ) { message.myInt64Value = Long.fromValue(object.myInt64Value) }
long(args: fromAminoMethod) {
const { origName } = getFieldNames(args.field);
TypeLong.addUtil(args.context);
const callExpr = t.callExpression(TypeLong.getFromString(args.context), [
t.memberExpression(t.identifier("object"), t.identifier(origName)),
]);
return setNotUndefinedAndNotNull(args, callExpr);
},
int64(args: fromAminoMethod) {
return fromAmino.long(args);
},
uint64(args: fromAminoMethod) {
return fromAmino.long(args);
},
sint64(args: fromAminoMethod) {
return fromAmino.long(args);
},
fixed64(args: fromAminoMethod) {
return fromAmino.long(args);
},
sfixed64(args: fromAminoMethod) {
return fromAmino.long(args);
},
protoType(args: fromAminoMethod) {
const { origName } = getFieldNames(args.field);
const name = args.context.getTypeName(args.field);
return setNotUndefinedAndNotNull(
args,
t.callExpression(
t.memberExpression(t.identifier(name), t.identifier("fromAmino")),
[t.memberExpression(t.identifier("object"), t.identifier(origName))]
)
);
},
anyType(args: fromAminoMethod) {
const { origName } = getFieldNames(args.field);
const interfaceName =
args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceFnName = getInterfaceFromAminoName(interfaceName);
let aminoFuncExpr: t.Expression = t.identifier(interfaceFnName);
const isGlobalRegistry =
args.context.options.interfaces?.useGlobalDecoderRegistry;
if (isGlobalRegistry) {
aminoFuncExpr = t.memberExpression(
t.identifier("GlobalDecoderRegistry"),
t.identifier("fromAminoMsg")
);
}
return setNotUndefinedAndNotNull(
args,
t.callExpression(aminoFuncExpr, [
t.memberExpression(t.identifier("object"), t.identifier(origName)),
])
);
},
type(args: fromAminoMethod) {
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === "google.protobuf.Any" &&
args.field.options["(cosmos_proto.accepts_interface)"]
) {
return fromAmino.anyType(args);
}
return fromAmino.protoType(args);
},
enum(args: fromAminoMethod) {
return setNotUndefinedAndNotNull(args);
},
bytes(args: fromAminoMethod) {
// bytes [RawContractMessage]
if (args.field.options?.["(gogoproto.casttype)"] === "RawContractMessage") {
return fromAmino.rawBytes(args);
}
// bytes [WASMByteCode]
// TODO use a better option for this in proto source
// field.options?.["(gogoproto.customname)"] === "WASMByteCode"
if (args.field.options?.["(gogoproto.customname)"] === "WASMByteCode") {
return fromAmino.wasmByteCode(args);
}
//default
args.context.addUtil("bytesFromBase64");
const { origName } = getFieldNames(args.field);
const expr = t.callExpression(t.identifier("bytesFromBase64"), [
t.memberExpression(t.identifier("object"), t.identifier(origName)),
]);
return setNotUndefinedAndNotNull(args, expr);
},
duration(args: fromAminoMethod) {
return fromAmino.type(args);
},
durationString(args: fromAminoMethod) {
return setNotUndefinedAndNotNull(args, t.identifier("undefined"));
},
timestamp(args: fromAminoMethod) {
const timestampFormat = args.context.pluginValue(
"prototypes.typingsFormat.timestamp"
);
switch (timestampFormat) {
case "timestamp":
return fromAmino.type(args);
case "date":
default:
return fromAmino.timestampDate(args);
}
},
timestampDate(args: fromAminoMethod) {
const { origName } = getFieldNames(args.field);
args.context.addUtil("fromTimestamp");
const expr = t.callExpression(t.identifier("fromTimestamp"), [
t.callExpression(
t.memberExpression(
t.identifier("Timestamp"),
t.identifier("fromAmino")
),
[t.memberExpression(t.identifier("object"), t.identifier(origName))]
),
]);
return setNotUndefinedAndNotNull(args, expr);
},
// message.referenceMap = Object.entries(object.referenceMap ?? {}).reduce<{
// [key: Long]: Reference;
// }>((acc, [key, value]) => {
// if (value !== undefined) {
// acc[Number(key)] = Reference.fromAmino(value);
// }
// return acc;
// }, {});
// message.labels = Object.entries(object.typeMap ?? {}).reduce<{
// [key: string]: string;
// }>((acc, [key, value]) => {
// if (value !== undefined) {
// acc[key] = String(value);
// }
// return acc;
// }, {});
keyHash(args: fromAminoMethod) {
const { propName, origName } = getFieldNames(args.field);
const keyType = args.field.keyType;
const valueType = args.field.parsedType.name;
let fromAminoWithArgs = null;
// valueTypeType: string for identifier
let valueTypeType = valueType;
switch (valueType) {
case "string":
fromAminoWithArgs = t.callExpression(t.identifier("String"), [
t.identifier("value"),
]);
break;
case "int32":
case "uint32":
valueTypeType = "number";
fromAminoWithArgs = t.callExpression(t.identifier("Number"), [
t.identifier("value"),
]);
break;
case "int64":
case "uint64":
case "sint64":
case "fixed64":
case "sfixed64":
TypeLong.addUtil(args.context);
valueTypeType = TypeLong.getPropType(args.context);
fromAminoWithArgs = TypeLong.getFromValueWithArgs(
args.context,
t.identifier("value")
);
break;
default:
fromAminoWithArgs = t.callExpression(
t.memberExpression(
t.identifier(valueType),
t.identifier("fromAmino")
),
[t.identifier("value")]
);
}
let wrapKey = null;
let keyTypeType = null;
switch (keyType) {
case "string":
wrapKey = (a) => a;
keyTypeType = t.tsStringKeyword();
break;
case "int64":
case "uint64":
case "sint64":
case "fixed64":
case "sfixed64":
wrapKey = (a) => t.callExpression(t.identifier("Number"), [a]);
TypeLong.addUtil(args.context);
keyTypeType = t.tsTypeReference(
TypeLong.getPropIdentifier(args.context)
);
break;
case "int32":
case "uint32":
wrapKey = (a) => t.callExpression(t.identifier("Number"), [a]);
keyTypeType = t.tsNumberKeyword();
break;
default:
throw new Error("keyHash requires new type. Ask maintainers.");
}
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("message"), t.identifier(propName)),
callExpression(
t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Object"),
t.identifier("entries")
),
[
t.logicalExpression(
"??",
t.memberExpression(
t.identifier("object"),
t.identifier(origName)
),
t.objectExpression([])
),
]
),
t.identifier("reduce")
),
[
t.arrowFunctionExpression(
[
t.identifier("acc"),
t.arrayPattern([t.identifier("key"), t.identifier("value")]),
],
t.blockStatement([
t.ifStatement(
t.binaryExpression(
"!==",
t.identifier("value"),
t.identifier("undefined")
),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("acc"),
wrapKey(t.identifier("key")),
true
),
fromAminoWithArgs
)
),
])
),
t.returnStatement(t.identifier("acc")),
])
),
t.objectExpression([]),
],
t.tsTypeParameterInstantiation([
t.tsTypeLiteral([
t.tsIndexSignature(
[identifier("key", t.tsTypeAnnotation(keyTypeType))],
t.tsTypeAnnotation(
t.tsTypeReference(t.identifier(valueTypeType))
)
),
]),
])
)
)
);
},
// message.codeIds = object.codeIds?.map(e => Long.fromValue(e)) || [];
array(args: fromAminoMethod, expr: t.Expression) {
const { origName, propName } = getFieldNames(args.field);
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("message"), t.identifier(propName)),
t.logicalExpression(
"||",
t.optionalCallExpression(
t.optionalMemberExpression(
t.memberExpression(
t.identifier("object"),
t.identifier(origName)
),
t.identifier("map"),
false,
true
),
[t.arrowFunctionExpression([t.identifier("e")], expr)],
false
),
t.arrayExpression([])
)
)
);
},
pubkey(args: fromAminoMethod) {
args.context.addUtil("encodePubkey");
const { origName } = getFieldNames(args.field);
const callExpr = t.callExpression(t.identifier("encodePubkey"), [
t.memberExpression(t.identifier("object"), t.identifier(origName)),
]);
return setNotUndefinedAndNotNull(args, callExpr);
},
rawBytes(args: fromAminoMethod) {
args.context.addUtil("toUtf8");
const { origName } = getFieldNames(args.field);
const expr = t.callExpression(t.identifier("toUtf8"), [
t.callExpression(
t.memberExpression(t.identifier("JSON"), t.identifier("stringify")),
[t.memberExpression(t.identifier("object"), t.identifier(origName))]
),
]);
return setNotUndefinedAndNotNull(args, expr);
},
wasmByteCode(args: fromAminoMethod) {
args.context.addUtil("fromBase64");
const { origName } = getFieldNames(args.field);
const expr = t.callExpression(t.identifier("fromBase64"), [
t.memberExpression(t.identifier("object"), t.identifier(origName)),
]);
return setNotUndefinedAndNotNull(args, expr);
},
};
export const arrayTypes = {
// message.overloadId = object.overloadId?.map(e => e) || [];
identity() {
return t.identifier("e");
},
string() {
return arrayTypes.identity();
},
bool() {
return arrayTypes.identity();
},
rawBytes(args: fromAminoMethod) {
args.context.addUtil("toUtf8");
return t.callExpression(t.identifier("toUtf8"), [
t.callExpression(
t.memberExpression(t.identifier("JSON"), t.identifier("stringify")),
[t.identifier("e")]
),
]);
},
wasmByteCode(args: fromAminoMethod) {
args.context.addUtil("fromBase64");
const { origName } = getFieldNames(args.field);
return t.callExpression(t.identifier("fromBase64"), [t.identifier("e")]);
},
bytes(args: fromAminoMethod) {
if (args.field.options?.["(gogoproto.casttype)"] === "RawContractMessage") {
return arrayTypes.rawBytes(args);
}
// bytes [WASMByteCode]
// field.options?.["(gogoproto.customname)"] === "WASMByteCode"
if (args.field.options?.["(gogoproto.customname)"] === "WASMByteCode") {
return arrayTypes.wasmByteCode(args);
}
// default
args.context.addUtil("bytesFromBase64");
return t.callExpression(t.identifier("bytesFromBase64"), [
t.identifier("e"),
]);
},
double() {
return arrayTypes.identity();
},
float() {
return arrayTypes.identity();
},
int32() {
return arrayTypes.identity();
},
uint32() {
return arrayTypes.identity();
},
sint32() {
return arrayTypes.identity();
},
fixed32() {
return arrayTypes.identity();
},
sfixed32() {
return arrayTypes.identity();
},
enum() {
return arrayTypes.identity();
},
// message.codeIds = object.codeIds?.map(e => Long.fromValue(e)) || [];
long(args: fromAminoMethod) {
TypeLong.addUtil(args.context);
return TypeLong.getFromStringArray(args.context);
},
int64(args: fromAminoMethod) {
return arrayTypes.long(args);
},
uint64(args: fromAminoMethod) {
return arrayTypes.long(args);
},
sint64(args: fromAminoMethod) {
return arrayTypes.long(args);
},
fixed64(args: fromAminoMethod) {
return arrayTypes.long(args);
},
sfixed64(args: fromAminoMethod) {
return arrayTypes.long(args);
},
anyType(args: fromAminoMethod) {
const interfaceName =
args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceFnName = getInterfaceFromAminoName(interfaceName);
let aminoFuncExpr: t.Expression = t.identifier(interfaceFnName);
const isGlobalRegistry =
args.context.options.interfaces?.useGlobalDecoderRegistry;
if (isGlobalRegistry) {
aminoFuncExpr = t.memberExpression(
t.identifier("GlobalDecoderRegistry"),
t.identifier("fromAminoMsg")
);
}
return t.callExpression(aminoFuncExpr, [t.identifier("e")]);
},
protoType(args: fromAminoMethod) {
const name = args.context.getTypeName(args.field);
return t.callExpression(
t.memberExpression(t.identifier(name), t.identifier("fromAmino")),
[t.identifier("e")]
);
},
type(args: fromAminoMethod) {
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === "google.protobuf.Any" &&
args.field.options["(cosmos_proto.accepts_interface)"]
) {
return arrayTypes.anyType(args);
}
return arrayTypes.protoType(args);
},
};
export const fromAminoMessages = {
anyType() {
return [
t.returnStatement(
t.objectExpression([
t.objectProperty(
t.identifier("typeUrl"),
t.memberExpression(t.identifier("object"), t.identifier("type"))
),
t.objectProperty(
t.identifier("value"),
t.memberExpression(t.identifier("object"), t.identifier("value"))
),
])
),
];
},
timestamp(context: ProtoParseContext, name: string, proto: ProtoType) {
context.addUtil("fromJsonTimestamp");
return [
t.returnStatement(
t.callExpression(t.identifier("fromJsonTimestamp"), [
t.identifier("object"),
])
),
];
},
height(context: ProtoParseContext, name: string, proto: ProtoType) {
TypeLong.addUtil(context);
const keepCase = context.options.prototypes.parser.keepCase;
const casing = keepCase ? (str) => str : camel;
const makeArgs = (fieldName: string) => {
let args: t.Expression[] = [
t.logicalExpression(
"||",
t.memberExpression(t.identifier("object"), t.identifier(fieldName)),
t.stringLiteral("0")
),
];
const longType = TypeLong.getType(context);
if (longType == "Long") {
args.push(t.booleanLiteral(true));
}
return args;
};
const makeField = (fieldName: string) =>
t.objectProperty(
t.identifier(casing(fieldName)),
t.callExpression(TypeLong.getFromString(context), makeArgs(fieldName))
);
return [
// return
t.returnStatement(
t.objectExpression([
makeField("revision_number"),
makeField("revision_height"),
])
),
];
},
duration(context: ProtoParseContext, name: string, proto: ProtoType) {
TypeLong.addUtil(context);
const longType = TypeLong.getType(context);
switch (longType) {
case "BigInt":
return [
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("value"),
t.callExpression(t.identifier("BigInt"), [t.identifier("object")])
),
]),
t.returnStatement(
t.objectExpression([
t.objectProperty(
t.identifier("seconds"),
t.binaryExpression(
"/",
t.identifier("value"),
t.callExpression(t.identifier("BigInt"), [
t.stringLiteral("1000000000"),
])
)
),
t.objectProperty(
t.identifier("nanos"),
t.callExpression(t.identifier("Number"), [
t.binaryExpression(
"%",
t.identifier("value"),
t.callExpression(t.identifier("BigInt"), [
t.stringLiteral("1000000000"),
])
),
])
),
])
),
];
case "Long":
default:
return [
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("value"),
t.callExpression(t.identifier("parseInt"), [
t.identifier("object"),
])
),
]),
// return
t.returnStatement(
t.objectExpression([
// seconds
t.objectProperty(
t.identifier("seconds"),
t.callExpression(TypeLong.getFromNumber(context), [
t.callExpression(
t.memberExpression(
t.identifier("Math"),
t.identifier("floor")
),
[t.binaryExpression("/", t.identifier("value"), BILLION)]
),
])
),
// nanos
t.objectProperty(
t.identifier("nanos"),
t.binaryExpression("%", t.identifier("value"), BILLION)
),
])
),
];
}
},
};