@cosmology/ast
Version:
Cosmos TypeScript AST generation
292 lines (255 loc) • 7.3 kB
text/typescript
import { ProtoType, ProtoField } from '@cosmology/types';
import { pascal } from 'case';
import { AminoParseContext, ProtoParseContext } from '../context';
import { shouldOmitEmpty } from '@cosmology/utils';
export const SCALAR_TYPES = [
'string',
'double',
'float',
'int32',
'uint32',
'sint32',
'fixed32',
'sfixed32',
'int64',
'uint64',
'sint64',
'fixed64',
'sfixed64',
'bytes',
'bool'
];
export const GOOGLE_TYPES = [
'google.protobuf.Timestamp',
'google.protobuf.Duration',
'google.protobuf.Any'
];
// https://github.com/protobufjs/protobuf.js/blob/master/src/types.js#L38-L54
export const types = {
basic: {
double: 1,
float: 5,
int32: 0,
uint32: 0,
sint32: 0,
fixed32: 5,
sfixed32: 5,
int64: 0,
uint64: 0,
sint64: 0,
fixed64: 1,
sfixed64: 1,
bool: 0,
string: 2,
bytes: 2
},
defaults: {
double: 0,
float: 0,
int32: 0,
uint32: 0,
sint32: 0,
fixed32: 0,
sfixed32: 0,
int64: 0,
uint64: 0,
sint64: 0,
fixed64: 0,
sfixed64: 0,
bool: false,
string: '',
bytes: [],
undefined: null
},
long: { int64: 0, uint64: 0, sint64: 0, fixed64: 1, sfixed64: 1 },
mapKey: {
int32: 0,
uint32: 0,
sint32: 0,
fixed32: 5,
sfixed32: 5,
int64: 0,
uint64: 0,
sint64: 0,
fixed64: 1,
sfixed64: 1,
bool: 0,
string: 2
},
packed: {
double: 1,
float: 5,
int32: 0,
uint32: 0,
sint32: 0,
fixed32: 5,
sfixed32: 5,
int64: 0,
uint64: 0,
sint64: 0,
fixed64: 1,
sfixed64: 1,
bool: 0
}
};
export const getWireNumber = (type) => {
if (types.basic.hasOwnProperty(type)) {
return types.basic[type];
}
return 2;
};
export const getPackedWireNumber = (type) => {
if (types.packed.hasOwnProperty(type)) {
return types.packed[type];
}
return 2;
};
export const getTagNumber = (field: ProtoField) => {
let wire = getWireNumber(field.type);
if (field.parsedType.type === 'Enum') {
wire = 0;
}
if (field.rule === 'repeated') {
wire = 2;
}
return ((field.id << 3) | wire) >>> 0;
};
const lowerFirst = (str: string) => {
return str.charAt(0).toLowerCase() + str.substring(1);
};
const upperFirst = (str: string) => {
return str.charAt(0).toUpperCase() + str.substring(1);
};
export const getEnumToJsonName = (name) => {
return lowerFirst(name) + 'ToJSON';
};
export const getEnumFromJsonName = (name) => {
return lowerFirst(name) + 'FromJSON';
};
export const getFieldsTypeName = (field: ProtoField) => {
if (!field?.scope || field?.scope.length <= 1) {
if(field.parsedType){
return field.parsedType.name;
} else {
const temp = field.type.split(".");
return temp[temp.length - 1];
}
}
const [_pkg, ...scopes] = field.scope;
return [...scopes, field.parsedType.name].join('_');
};
export const getKeyTypeEntryName = (typeName: string, prop: string) => {
return `${typeName}_${pascal(prop)}Entry`;
};
export const getBaseCreateTypeFuncName = (name) => {
return `createBase${upperFirst(name)}`;
};
export const getOneOfs = (type: ProtoType) => {
const keys = Object.keys(type.oneofs ?? {});
if (!keys.length) return [];
if (keys.length !== 1) throw new Error('getOneOfs() oneofs cardinality not known!');
return type.oneofs[keys[0]].oneof;
};
export const getFieldOptionality = (
context: ProtoParseContext | AminoParseContext,
field: ProtoField,
isOneOf: boolean
) => {
const isScalarDefaultToNullable = context.pluginValue('prototypes.isScalarDefaultToNullable');
if(field?.options?.['(gogoproto.moretags)'] && getOptionDeprecated(field?.options?.['(gogoproto.moretags)']) === 'true'){
return true
}
if (isArrayField(field) || isEnumField(field) || (!isScalarDefaultToNullable && isScalarField(field))) {
// these field types are required by default!
if (isOneOf) {
return true;
}
return false;
}
return true;
};
export const getFieldOptionalityForAmino = (
context: ProtoParseContext | AminoParseContext,
field: ProtoField,
isOneOf: boolean
) => {
const useProtoOptionality = context.pluginValue('aminoEncoding.useProtoOptionality');
if(field?.options?.['(gogoproto.moretags)'] && getOptionDeprecated(field?.options?.['(gogoproto.moretags)']) === 'true'){
return true
}
if(useProtoOptionality){
return getFieldOptionalityForDefaults(context, field, isOneOf);
}
if (isOneOf) {
return true;
}
return shouldOmitEmpty(context, field)
};
export const isScalarField = (
field: ProtoField
) => {
return SCALAR_TYPES.includes(field.type);
};
export const isArrayField = (
field: ProtoField
) => {
return field.rule === 'repeated';
};
export const isEnumField = (
field: ProtoField
) => {
return field.parsedType?.type === 'Enum'
};
export const isMapField = (
field: ProtoField
) => {
return field.keyType;
};
export const getFieldOptionalityForDefaults = (
context: ProtoParseContext | AminoParseContext,
field: ProtoField,
isOneOf: boolean
) => {
const fieldDefaultIsOptional = context.pluginValue('prototypes.fieldDefaultIsOptional');
const useOptionalNullable = context.pluginValue('prototypes.useOptionalNullable');
const isScalarDefaultToNullable = !field?.options?.['(gogoproto.nullable)'] && context.pluginValue('prototypes.isScalarDefaultToNullable');
if(field?.options?.['(gogoproto.moretags)'] && getOptionDeprecated(field?.options?.['(gogoproto.moretags)']) === 'true'){
return true
}
if (isArrayField(field) || isEnumField(field) || !isScalarDefaultToNullable && isScalarField(field) || isMapField(field)) {
// these field types are required by default!
if (isOneOf || (useOptionalNullable &&
field?.options?.['(gogoproto.nullable)'])) {
return true;
}
return false;
}
const gogoprotoNullable = field?.options?.['(gogoproto.nullable)'] ?? true;
return isOneOf ||
(
useOptionalNullable &&
gogoprotoNullable
)
||
(
// this would only happen if previous predicate is false,
// so lets ensure not to override required properties when gogoproto.nullable=false
!useOptionalNullable &&
fieldDefaultIsOptional
);
};
/**
* get deprecated option from string, return true/false as string
* @param input
* @returns string
*/
export const getOptionDeprecated = (input: string) =>{
// Regular expression to match deprecated value with optional spaces
const deprecatedRegex = /\s*deprecated:\s*(true|false)\s*/;
// Extract the deprecated value
const match = input.match(deprecatedRegex);
if(match && match[1]) {
return match[1]
}
return null
}