@cosmology/ast
Version:
Cosmos TypeScript AST generation
241 lines (203 loc) • 7.13 kB
text/typescript
import { TelescopeOptions, ProtoField, ProtoRef, TraversalSymbol, IParseContext, ImportUsage } from '@cosmology/types';
import { IProtoStore } from '@cosmology/types';
import { isRefExcluded, getObjectName } from '@cosmology/utils';
import { getEnumFromJsonName, getEnumToJsonName, getFieldsTypeName } from './proto';
import { getPluginValue } from '../plugins';
import { TelescopeBaseTypes } from './types';
interface DerivativeImport {
type: TelescopeBaseTypes,
symbol: TraversalSymbol
}
export class GenericParseContext implements IParseContext {
options: TelescopeOptions;
imports: ImportUsage[] = [];
derivedImports: DerivativeImport[] = [];
utils: Record<string, boolean> = {};
store: IProtoStore;
ref: ProtoRef;
constructor(
ref: ProtoRef,
store: IProtoStore,
options: TelescopeOptions
) {
this.ref = ref;
this.store = store;
this.options = options;
if (!this.options) {
throw new Error('ParseContext requires options!');
}
}
pluginValue(name) {
return getPluginValue(name, this.ref?.proto?.package, this.options);
}
isExcluded() {
const excluded = this.pluginValue('prototypes.excluded');
return isRefExcluded(this.ref, excluded)
}
addUtil(util) {
this.utils[util] = true;
}
addImport(imp: ImportUsage) {
// some local lookups don't have an import (local proto-style lookups do)
if (!imp.import) return;
this.imports.push(imp)
}
addImportDerivative(imp: DerivativeImport) {
const found = this.derivedImports.find(a => {
return a.type === imp.type &&
a.symbol.symbolName === imp.symbol.symbolName &&
a.symbol.source === imp.symbol.source;
});
if (!found) {
this.derivedImports.push(imp);
}
}
getTypeNameFromFieldName(name: string, importSrc: string) {
let importedAs = name;
const names = this.ref.traversed?.importNames;
if (names
&& names.hasOwnProperty(importSrc)
&& names[importSrc].hasOwnProperty(name)
) {
importedAs = names[importSrc][name];
}
this.addImport({
type: 'typeImport',
name,
importedAs,
import: importSrc
})
return importedAs;
}
getTypeName(field: ProtoField) {
let name = getFieldsTypeName(field);
return this.getTypeNameFromFieldName(name, field.import);
}
lookupTypeFromCurrentPath(field: ProtoField, currentProtoPath: string) {
const ref = this.store.findProto(currentProtoPath);
let lookup = this.store.get(ref, field.parsedType.name);
if (!lookup) {
// if we can't find it, use field import
if (field.import) {
const importRef = this.store.findProto(field.import);
if (!importRef) {
throw new Error(`bad import ${field.import}`);
}
lookup = this.store.get(importRef, field.parsedType.name);
}
if (!lookup) {
throw new Error('Undefined Symbol: ' + field.parsedType.name);
}
}
return lookup;
}
getTypeFromCurrentPath(field: ProtoField, currentProtoPath: string) {
const ref = this.store.findProto(currentProtoPath);
let lookup = this.store.get(ref, field.parsedType.name);
if (!lookup) {
// if we can't find it, use field import
if (field.import) {
const importRef = this.store.findProto(field.import);
if (!importRef) {
throw new Error(`bad import ${field.import}`);
}
lookup = this.store.get(importRef, field.parsedType.name);
}
if (!lookup) {
throw new Error('Undefined Symbol: ' + field.parsedType.name);
}
}
this.addImport(
{
type: 'typeImport',
name: lookup.importedName,
import: lookup.import
}
)
return lookup.obj;
}
}
export class AminoParseContext extends GenericParseContext implements IParseContext {
aminoCasingFn: Function;
constructor(
ref: ProtoRef,
store: IProtoStore,
options: TelescopeOptions
) {
super(ref, store, options);
this.ref = ref;
this.store = store;
this.options = options;
this.setAminoCasingFn();
if (!this.aminoCasingFn) {
throw new Error('missing aminoCasingFn!')
}
this.aminoCaseField = this.aminoCaseField.bind(this);
}
private setAminoCasingFn() {
if (this.aminoCasingFn) return this.aminoCasingFn;
this.aminoCasingFn = this.pluginValue('aminoEncoding.casingFn');
return this.aminoCasingFn;
}
aminoCaseField(field: ProtoField) {
return field.options['(telescope:orig)'];
}
lookupEnumFromJson(field: ProtoField, currentProtoPath: string) {
const lookup = this.lookupTypeFromCurrentPath(field, currentProtoPath);
const Enum = lookup.obj;
const name = getEnumFromJsonName(getObjectName(Enum.name, Enum.scope));
this.addImport({
type: 'fromJSONEnum',
name,
import: lookup.import
});
return name;
}
lookupEnumToJson(field: ProtoField, currentProtoPath: string) {
const lookup = this.lookupTypeFromCurrentPath(field, currentProtoPath);
const Enum = lookup.obj;
const name = getEnumToJsonName(getObjectName(Enum.name, Enum.scope));
this.addImport({
type: 'toJSONEnum',
name,
import: lookup.import
});
return name;
}
}
export class ProtoParseContext extends GenericParseContext implements IParseContext {
constructor(
ref: ProtoRef,
store: IProtoStore,
options: TelescopeOptions
) {
super(ref, store, options);
this.ref = ref;
this.store = store;
this.options = options;
}
setEnumValues(pkg: string, name: string, protoSyntex: string, values: number[]) {
this.store.setEnumValues(pkg, name, protoSyntex, values);
}
getDefaultOrExistingSmallestEnumValue(pkg: string, name: string) {
return this.store.getDefaultOrExistingSmallestEnumValue(pkg, name);
}
getToEnum(field: ProtoField) {
const name = getEnumToJsonName(getFieldsTypeName(field));
this.addImport({
type: 'toJSONEnum',
name,
import: field.import
});
return name;
}
getFromEnum(field: ProtoField) {
const fromJSONFuncName = getEnumFromJsonName(getFieldsTypeName(field));
this.addImport({
type: 'fromJSONEnum',
name: fromJSONFuncName,
import: field.import
});
return fromJSONFuncName;
}
}