@cosmology/ast
Version:
Cosmos TypeScript AST generation
711 lines (650 loc) • 23.2 kB
text/typescript
import * as t from '@babel/types';
import { ProtoField } from '@cosmology/types';
import { getDefaultTSTypeFromProtoType } from '../../types';
import { ToJSONMethod } from './index';
import { getFieldOptionalityForDefaults } from '../types';
const notUndefinedSetValue = (messageProp: string, objProp: string, expr: t.Expression) => {
return t.expressionStatement(
t.logicalExpression(
'&&',
t.binaryExpression(
'!==',
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.identifier('undefined')
),
t.assignmentExpression(
'=',
t.memberExpression(
t.identifier('obj'),
t.identifier(objProp)
),
expr
)
)
);
}
const getPropNames = (field: ProtoField) => {
const messageProp = field.name;
const objProp = field.options?.json_name ?? field.name;
return {
messageProp,
objProp
}
}
export const toJSON = {
// message.sender !== undefined && (obj.sender = message.sender);
identity(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
return notUndefinedSetValue(messageProp, objProp, t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
));
},
string(args: ToJSONMethod) {
return toJSON.identity(args);
},
double(args: ToJSONMethod) {
return toJSON.identity(args);
},
float(args: ToJSONMethod) {
return toJSON.identity(args);
},
bool(args: ToJSONMethod) {
return toJSON.identity(args);
},
// message.maxDepth !== undefined && (obj.maxDepth = Math.round(message.maxDepth));
number(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
return notUndefinedSetValue(
messageProp,
objProp,
t.callExpression(
t.memberExpression(
t.identifier('Math'),
t.identifier('round')
),
[
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]
)
);
},
// message.maxDepth !== undefined && (obj.maxDepth = Math.round(message.maxDepth));
int32(args: ToJSONMethod) {
return toJSON.number(args);
},
uint32(args: ToJSONMethod) {
return toJSON.number(args);
},
sint32(args: ToJSONMethod) {
return toJSON.number(args);
},
fixed32(args: ToJSONMethod) {
return toJSON.number(args);
},
sfixed32(args: ToJSONMethod) {
return toJSON.number(args);
},
// message.poolId !== undefined && (obj.poolId = (message.poolId || Long.UZERO).toString());
// message.poolId !== undefined && (obj.poolId = (message.poolId || undefined).toString());
long(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
const isOptional = getFieldOptionalityForDefaults(args.context, args.field, args.isOneOf);
if(isOptional){
return t.ifStatement(
t.binaryExpression(
"!==",
t.memberExpression(t.identifier("message"), t.identifier(messageProp)),
t.identifier("undefined")
),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(objProp)),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(messageProp)
),
t.identifier("toString")
),
[]
)
)
),
])
);
} else {
return notUndefinedSetValue(
messageProp,
objProp,
t.callExpression(
t.memberExpression(
t.logicalExpression(
'||',
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf)
),
t.identifier('toString')
),
[]
)
);
}
},
int64(args: ToJSONMethod) {
return toJSON.long(args);
},
uint64(args: ToJSONMethod) {
return toJSON.long(args);
},
sint64(args: ToJSONMethod) {
return toJSON.long(args);
},
fixed64(args: ToJSONMethod) {
return toJSON.long(args);
},
sfixed64(args: ToJSONMethod) {
return toJSON.long(args);
},
// message.signDoc !== undefined && (obj.signDoc = message.signDoc ? SignDocDirectAux.toJSON(message.signDoc) : undefined);
type(args: ToJSONMethod) {
let name = args.context.getTypeName(args.field);
const { messageProp, objProp } = getPropNames(args.field);
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.context.options.interfaces?.useGlobalDecoderRegistry &&
args.field.type === 'google.protobuf.Any' &&
args.field.options['(cosmos_proto.accepts_interface)']
) {
name = 'GlobalDecoderRegistry';
}
// TODO isn't the nested conditional a waste? (using ts-proto as reference)
// maybe null is OK?
return notUndefinedSetValue(messageProp, objProp, t.conditionalExpression(
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.callExpression(
t.memberExpression(
t.identifier(name),
t.identifier('toJSON')
),
[
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]
),
t.identifier('undefined')
));
},
// message.mode !== undefined && (obj.mode = signModeToJSON(message.mode));
enum(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
const enumFuncName = args.context.getToEnum(args.field);
return notUndefinedSetValue(messageProp, objProp, t.callExpression(
t.identifier(enumFuncName),
[
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]
));
},
// TODO again, another ts-proto reference that does not necessarily make sense
// message.queryData !== undefined && (obj.queryData = base64FromBytes(message.queryData !== undefined ? message.queryData : new Uint8Array()));
// message.queryData !== undefined && (obj.queryData = base64FromBytes(message.queryData !== undefined ? message.queryData : undefined));
bytes(args: ToJSONMethod) {
args.context.addUtil('base64FromBytes');
const { messageProp, objProp } = getPropNames(args.field);
let expr;
if (args.isOptional) {
// message.bytesValue !== undefined && (obj.bytesValue = message.bytesValue !== undefined ? base64FromBytes(message.bytesValue) : undefined);
expr = t.conditionalExpression(
t.binaryExpression(
'!==',
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.identifier('undefined')
),
t.callExpression(
t.identifier('base64FromBytes'),
[
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]
),
t.identifier('undefined')
);
} else {
// message.queryData !== undefined && (obj.queryData = base64FromBytes(message.queryData !== undefined ? message.queryData : new Uint8Array()));
expr = t.callExpression(
t.identifier('base64FromBytes'),
[
t.conditionalExpression(
t.binaryExpression(
'!==',
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.identifier('undefined')
),
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf)
)
]
)
}
return notUndefinedSetValue(messageProp, objProp, expr);
},
// message.period !== undefined && (obj.period = message.period);
duration(args: ToJSONMethod) {
const durationFormat = args.context.pluginValue('prototypes.typingsFormat.duration');
switch (durationFormat) {
case 'string':
return toJSON.durationString(args);
case 'duration':
default:
return toJSON.type(args);
}
},
durationString(args: ToJSONMethod) {
return toJSON.identity(args);
},
timestamp(args: ToJSONMethod) {
const timestampFormat = args.context.pluginValue('prototypes.typingsFormat.timestamp')
switch (timestampFormat) {
case 'timestamp':
return toJSON.timestampTimestamp(args);
case 'date':
default:
return toJSON.timestampDate(args);
}
},
// message.periodReset !== undefined && (obj.periodReset = fromTimestamp(message.periodReset).toISOString());
timestampTimestamp(args: ToJSONMethod) {
args.context.addUtil('fromTimestamp');
const { messageProp, objProp } = getPropNames(args.field);
return notUndefinedSetValue(messageProp, objProp, t.callExpression(
t.memberExpression(
t.callExpression(
t.identifier('fromTimestamp'), [
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]),
t.identifier('toISOString')
),
[]
));
},
// message.periodReset !== undefined && (obj.periodReset = message.periodReset.toISOString());
timestampDate(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
return notUndefinedSetValue(messageProp, objProp, t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.identifier('toISOString')
),
[]
));
},
// obj.labels = {};
// if (message.labels) {
// Object.entries(message.labels).forEach(([k, v]) => {
// obj.labels[k] = v;
// });
// }
// obj.typeMap = {};
// if (message.typeMap) {
// Object.entries(message.typeMap).forEach(([k, v]) => {
// obj.typeMap[k] = Type.toJSON(v);
// });
// }
keyHash(args: ToJSONMethod) {
const { messageProp, objProp } = getPropNames(args.field);
const keyType = args.field.keyType;
const valueType = args.field.parsedType.name;
let toJSON = null;
switch (valueType) {
case 'string':
toJSON = t.identifier('v')
break;
case 'uint32':
case 'int32':
toJSON = t.callExpression(
t.memberExpression(
t.identifier('Math'),
t.identifier('round')
),
[
t.identifier('v')
]
)
break;
case 'int64':
case 'uint64':
toJSON = t.callExpression(
t.memberExpression(
t.identifier('v'),
t.identifier('toString')
),
[]
)
break;
default:
toJSON = t.callExpression(
t.memberExpression(
t.identifier(valueType),
t.identifier('toJSON')
),
[
t.identifier('v')
]
)
}
return [
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.identifier('obj'),
t.identifier(objProp)
),
t.objectExpression([])
)
),
//
t.ifStatement(
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.blockStatement([
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier('Object'),
t.identifier('entries')
),
[
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
)
]
),
t.identifier('forEach')
),
[
t.arrowFunctionExpression(
[
t.arrayPattern(
[
t.identifier('k'),
t.identifier('v')
]
)
],
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.memberExpression(
t.identifier('obj'),
t.identifier(objProp)
),
t.identifier('k'),
true
),
toJSON
)
)
])
)
]
)
)
])
)
]
},
// if (message.codeIds) {
// obj.codeIds = message.codeIds.map(e => (e || Long.UZERO).toString());
// } else {
// obj.codeIds = [];
// }
array(args: ToJSONMethod, expr: t.Expression) {
const { messageProp, objProp } = getPropNames(args.field);
return t.ifStatement(
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.identifier('obj'),
t.identifier(objProp)
),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier('message'),
t.identifier(messageProp)
),
t.identifier('map')
),
[
t.arrowFunctionExpression(
[
t.identifier('e')
],
expr
)
]
)
)
)
]),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.identifier('obj'),
t.identifier(objProp)
),
t.arrayExpression([])
)
)
])
);
}
};
export const arrayTypes = {
identity() {
return t.identifier('e');
},
// if (message.overloadId) {
// obj.overloadId = message.overloadId.map(e => e);
// } else {
// obj.overloadId = [];
// }
string() {
return arrayTypes.identity();
},
double() {
return arrayTypes.identity();
},
float() {
return arrayTypes.identity();
},
bool() {
return arrayTypes.identity();
},
// if (message.lineOffsets) {
// obj.lineOffsets = message.lineOffsets.map(e => Math.round(e));
// } else {
// obj.lineOffsets = [];
// }
number() {
return t.callExpression(
t.memberExpression(
t.identifier('Math'),
t.identifier('round')
),
[
t.identifier('e')
]
)
},
int32() {
return arrayTypes.number();
},
uint32() {
return arrayTypes.number();
},
sint32() {
return arrayTypes.number();
},
fixed32() {
return arrayTypes.number();
},
sfixed32() {
return arrayTypes.number();
},
// if (message.codeIds) {
// obj.codeIds = message.codeIds.map(e => (e || Long.UZERO).toString());
// } else {
// obj.codeIds = [];
// }
long(args: ToJSONMethod) {
return t.callExpression(
t.memberExpression(
t.logicalExpression(
'||',
t.identifier('e'),
getDefaultTSTypeFromProtoType(args.context, {
...args.field,
rule: undefined, // so it's treated as type not an array...
}, args.isOneOf)
),
t.identifier('toString')
),
[]
)
},
int64(args: ToJSONMethod) {
return arrayTypes.long(args);
},
uint64(args: ToJSONMethod) {
return arrayTypes.long(args);
},
sint64(args: ToJSONMethod) {
return arrayTypes.long(args);
},
fixed64(args: ToJSONMethod) {
return arrayTypes.long(args);
},
sfixed64(args: ToJSONMethod) {
return arrayTypes.long(args);
},
// if (message.myBytesArray) {
// obj.myBytesArray = message.myBytesArray.map(e => base64FromBytes(e !== undefined ? e : new Uint8Array()));
// } else {
// obj.myBytesArray = [];
// }
bytes(args: ToJSONMethod) {
args.context.addUtil('base64FromBytes');
return t.callExpression(
t.identifier('base64FromBytes'),
[
t.conditionalExpression(
t.binaryExpression(
'!==',
t.identifier('e'),
t.identifier('undefined')
),
t.identifier('e'),
getDefaultTSTypeFromProtoType(args.context, {
...args.field,
rule: undefined, // so it's treated as type not an array...
}, args.isOneOf)
)
]
);
},
enum(args: ToJSONMethod) {
const enumFuncName = args.context.getToEnum(args.field);
return t.callExpression(
t.identifier(enumFuncName),
[
t.identifier('e')
]
);
},
// if (message.tokenInMaxs) {
// obj.tokenInMaxs = message.tokenInMaxs.map(e => e ? Coin.toJSON(e) : undefined);
// } else {
// obj.tokenInMaxs = [];
// }
type(args: ToJSONMethod) {
let name = args.context.getTypeName(args.field);
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.context.options.interfaces?.useGlobalDecoderRegistry &&
args.field.type === 'google.protobuf.Any' &&
args.field.options['(cosmos_proto.accepts_interface)']
) {
name = 'GlobalDecoderRegistry';
}
return t.conditionalExpression(
t.identifier('e'),
t.callExpression(
t.memberExpression(
t.identifier(name),
t.identifier('toJSON')
),
[
t.identifier('e')
]
),
t.identifier('undefined')
);
}
}