@cosmology/ast
Version:
Cosmos TypeScript AST generation
529 lines (479 loc) • 17.2 kB
text/typescript
import * as t from "@babel/types";
import {
BILLION,
memberExpressionOrIdentifier,
shorthandProperty,
TypeLong,
} from "../../../utils";
import { protoFieldsToArray } from "../utils";
import { ToAminoParseField, toAminoParseField } from "./index";
import { getFieldOptionality, getOneOfs } from "../../proto";
import { ProtoField } from "@cosmology/types";
export const toAmino = {
defaultType(args: ToAminoParseField, omitEmpty?: boolean) {
const useOmitEmpty = !!args.context.pluginValue(
"aminoEncoding.legacy.useOmitEmpty"
);
if (
!(useOmitEmpty && omitEmpty) &&
args.field.name === args.context.aminoCaseField(args.field) &&
args.scope.length === 1
) {
return shorthandProperty(args.field.name);
}
let valueExpr =
useOmitEmpty && omitEmpty
? this.omitDefaultMemberExpressionOrIdentifier(args, args.scope)
: memberExpressionOrIdentifier(args.scope);
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
valueExpr
);
},
long(args: ToAminoParseField, omitEmpty?: boolean) {
const useOmitEmpty = !!args.context.pluginValue(
"aminoEncoding.legacy.useOmitEmpty"
);
const useNullHandling = !!args.context.pluginValue(
"aminoEncoding.legacy.useNullHandling"
);
let valueExpr =
useOmitEmpty && omitEmpty
? this.omitDefaultMemberExpressionOrIdentifier(args, args.scope)
: memberExpressionOrIdentifier(args.scope);
const callExpression = useNullHandling
? t.optionalCallExpression
: t.callExpression;
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
callExpression(
useNullHandling
? t.optionalMemberExpression(
valueExpr,
t.identifier("toString"),
false,
true
)
: t.memberExpression(valueExpr, t.identifier("toString")),
[],
true
)
);
},
string(args: ToAminoParseField, omitEmpty?: boolean) {
const useCosmosSDKDec = args.context.pluginValue(
"aminoEncoding.customTypes.useCosmosSDKDec"
);
const goPackage = args.context.ref.proto.options?.["go_package"];
if (useCosmosSDKDec) {
const isCosmosSDKDec =
(goPackage == "github.com/cosmos/cosmos-sdk/types" &&
args.field.options?.["(gogoproto.customtype)"] == "Dec") ||
args.field.options?.["(gogoproto.customtype)"] ==
"github.com/cosmos/cosmos-sdk/types.Dec" ||
args.field.options?.["(gogoproto.customtype)"] ==
"cosmossdk.io/math.LegacyDec";
if (isCosmosSDKDec) {
args.context.addUtil("Decimal");
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Decimal"),
t.identifier("fromUserInput")
),
[
memberExpressionOrIdentifier(args.scope),
t.numericLiteral(18),
]
),
t.identifier("atomics")
)
);
}
}
if (
args.field.name === args.context.aminoCaseField(args.field) &&
args.scope.length === 1
) {
return shorthandProperty(args.field.name);
}
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
memberExpressionOrIdentifier(args.scope)
);
},
stringArray(args: ToAminoParseField) {
const useCosmosSDKDec = args.context.pluginValue(
"aminoEncoding.customTypes.useCosmosSDKDec"
);
if (useCosmosSDKDec) {
const goPackage = args.context.ref.proto.options?.["go_package"];
const isCosmosSDKDec =
(goPackage == "github.com/cosmos/cosmos-sdk/types" &&
args.field.options?.["(gogoproto.customtype)"] == "Dec") ||
args.field.options?.["(gogoproto.customtype)"] ==
"github.com/cosmos/cosmos-sdk/types.Dec" ||
args.field.options?.["(gogoproto.customtype)"] ==
"cosmossdk.io/math.LegacyDec";
if (isCosmosSDKDec) {
return toAmino.scalarArray(args, arrayTypes.stringDec);
}
}
if (
args.field.name === args.context.aminoCaseField(args.field) &&
args.scope.length === 1
) {
return shorthandProperty(args.field.name);
}
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
memberExpressionOrIdentifier(args.scope)
);
},
rawBytes(args: ToAminoParseField) {
args.context.addUtil("fromUtf8");
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
t.callExpression(
t.memberExpression(t.identifier("JSON"), t.identifier("parse")),
[
t.callExpression(t.identifier("fromUtf8"), [
memberExpressionOrIdentifier(args.scope),
]),
]
)
);
},
wasmByteCode(args: ToAminoParseField) {
args.context.addUtil("toBase64");
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
t.callExpression(t.identifier("toBase64"), [
memberExpressionOrIdentifier(args.scope),
])
);
},
duration(args: ToAminoParseField) {
const durationFormat = args.context.pluginValue(
"prototypes.typingsFormat.duration"
);
const updatedDuration = args.context.pluginValue(
"prototypes.typingsFormat.updatedDuration"
);
switch (durationFormat) {
case "duration":
if (updatedDuration) {
return toAmino.durationType(args);
}
case "string":
default:
return toAmino.durationString(args);
}
},
// (duration * 1_000_000_000).toString(),
durationString(args: ToAminoParseField) {
const exp = t.binaryExpression(
"*",
memberExpressionOrIdentifier(args.scope),
BILLION
);
exp.extra = { parenthesized: true };
const value = t.callExpression(
t.memberExpression(exp, t.identifier("toString")),
[]
);
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
value
);
},
// (duration.seconds.toInt() * 1_000_000_000).toString(),
// what about nanos?
durationType(args: ToAminoParseField) {
const exp = t.binaryExpression(
"*",
t.callExpression(
t.memberExpression(
t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("seconds")
),
t.identifier("toInt")
),
[]
),
BILLION
);
exp.extra = { parenthesized: true };
const value = t.callExpression(
t.memberExpression(exp, t.identifier("toString")),
[]
);
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
value
);
},
height(args: ToAminoParseField) {
args.context.addUtil("omitDefault");
const value = t.objectExpression([
t.objectProperty(
t.identifier(args.context.aminoCasingFn("revision_height")),
t.optionalCallExpression(
t.optionalMemberExpression(
t.callExpression(t.identifier("omitDefault"), [
t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("revisionHeight")
),
]),
t.identifier("toString"),
false,
true
),
[],
false
)
),
//
t.objectProperty(
t.identifier(args.context.aminoCasingFn("revision_number")),
t.optionalCallExpression(
t.optionalMemberExpression(
t.callExpression(t.identifier("omitDefault"), [
t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("revisionNumber")
),
]),
t.identifier("toString"),
false,
true
),
[],
false
)
),
]);
const cond = t.conditionalExpression(
memberExpressionOrIdentifier(args.scope),
value,
t.objectExpression([])
);
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
cond
);
},
coinAmount(args: ToAminoParseField) {
const longType = TypeLong.getType(args.context);
switch (longType) {
case "BigInt":
return t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("amount")
);
case "Long":
default:
TypeLong.addUtil(args.context);
return t.callExpression(
t.memberExpression(
t.callExpression(TypeLong.getFromValue(args.context), [
t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("amount")
),
]),
t.identifier("toString")
),
[]
);
}
},
coin(args: ToAminoParseField) {
const value = t.objectExpression([
t.objectProperty(
t.identifier("denom"),
t.memberExpression(
memberExpressionOrIdentifier(args.scope),
t.identifier("denom")
)
),
t.objectProperty(t.identifier("amount"), toAmino.coinAmount(args)),
]);
return t.objectProperty(
t.identifier(args.context.aminoCaseField(args.field)),
value
);
},
type({
context,
field,
currentProtoPath,
scope,
fieldPath,
nested,
isOptional,
}: ToAminoParseField) {
/// TODO (can this be refactored out? e.g. no recursive calls in this file?)
/// BEGIN
const Type = context.getTypeFromCurrentPath(field, currentProtoPath);
const parentField = field;
const oneOfs = getOneOfs(Type);
const properties = protoFieldsToArray(Type).map((field) => {
const isOneOf = oneOfs.includes(field.name);
const isOptional = getFieldOptionality(context, field, isOneOf);
if (parentField.import) currentProtoPath = parentField.import;
return toAminoParseField({
context,
field,
currentProtoPath,
scope: [...scope],
fieldPath: [...fieldPath],
nested,
isOptional, // TODO how to handle nested optionality
});
});
/// END
return t.objectProperty(
t.identifier(context.aminoCaseField(field)),
t.objectExpression(properties)
);
},
typeArray({
context,
field,
currentProtoPath,
scope,
fieldPath,
nested,
isOptional,
}: ToAminoParseField) {
//////
const variable = "el" + nested;
const f = JSON.parse(JSON.stringify(field)); // clone
const varProto: ProtoField = {
...f,
};
varProto.name = variable;
varProto.options["(telescope:orig)"] = variable;
varProto.options["(telescope:name)"] = variable;
varProto.options["(telescope:camel)"] = variable;
//////
if (field.parsedType.type !== "Type") {
throw new Error("Arrays only support types[Type] right now.");
}
const Type = context.getTypeFromCurrentPath(field, currentProtoPath);
const parentField = field;
const oneOfs = getOneOfs(Type);
const properties = protoFieldsToArray(Type).map((field) => {
const isOneOf = oneOfs.includes(field.name);
const isOptional = getFieldOptionality(context, field, isOneOf);
if (parentField.import) currentProtoPath = parentField.import;
return toAminoParseField({
context,
field,
currentProtoPath,
scope: [variable],
fieldPath: [varProto],
nested: nested + 1,
isOptional, // TODO how to handle nested optionality
});
});
const expr = t.callExpression(
t.memberExpression(
memberExpressionOrIdentifier(scope),
t.identifier("map")
),
[
t.arrowFunctionExpression(
[t.identifier(variable)],
t.objectExpression(properties)
),
]
);
return t.objectProperty(
t.identifier(context.aminoCaseField(field)),
expr
);
},
scalarArray(
{
context,
field,
currentProtoPath,
scope,
nested,
isOptional,
}: ToAminoParseField,
arrayTypeAstFunc: Function
) {
const variable = "el" + nested;
const expr = t.callExpression(
t.memberExpression(
memberExpressionOrIdentifier(scope),
t.identifier("map")
),
[
t.arrowFunctionExpression(
[t.identifier(variable)],
arrayTypeAstFunc(variable, {
context,
field,
currentProtoPath,
scope,
nested,
isOptional,
})
),
]
);
return t.objectProperty(
t.identifier(context.aminoCaseField(field)),
expr
);
},
pubkey(args: ToAminoParseField) {
args.context.addUtil("decodePubkey");
return t.objectProperty(
t.identifier(args.field.name),
t.tsNonNullExpression(
t.callExpression(t.identifier("decodePubkey"), [
t.identifier(args.field.name),
])
)
);
},
omitDefaultMemberExpressionOrIdentifier(args: ToAminoParseField, names) {
args.context.addUtil("omitDefault");
return t.callExpression(t.identifier("omitDefault"), [
memberExpressionOrIdentifier(names),
]);
},
};
export const arrayTypes = {
long(varname: string) {
return t.callExpression(
t.memberExpression(
memberExpressionOrIdentifier([varname]),
t.identifier("toString")
),
[]
);
},
stringDec(varname: string, args: ToAminoParseField) {
args.context.addUtil("Decimal");
return t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Decimal"),
t.identifier("fromUserInput")
),
[memberExpressionOrIdentifier([varname]), t.numericLiteral(18)]
),
t.identifier("atomics")
);
},
};