Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: convert Go to new constraint setup #732

Merged
merged 3 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 48 additions & 195 deletions src/generators/go/GoConstrainer.ts
Original file line number Diff line number Diff line change
@@ -1,200 +1,53 @@
import { ConstrainedAnyModel, ConstrainedBooleanModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedArrayModel, ConstrainedUnionModel, ConstrainedEnumModel, ConstrainedDictionaryModel, ConstrainedEnumValueModel } from '../../models/ConstrainedMetaModel';
import { AnyModel, BooleanModel, FloatModel, IntegerModel, MetaModel, ObjectModel, ReferenceModel, StringModel, TupleModel, ArrayModel, UnionModel, EnumModel, DictionaryModel } from '../../models/MetaModel';
import { defaultPropertyKeyConstraints, PropertyKeyConstraintType } from './constrainer/PropertyKeyConstrainer';
import { defaultModelNameConstraints, ModelNameConstraintType } from './constrainer/ModelNameConstrainer';
import { defaultEnumKeyConstraints, EnumConstraintType } from './constrainer/EnumConstrainer';

export interface GoConstraints {
enumKey: EnumConstraintType,
modelName: ModelNameConstraintType,
propertyKey: PropertyKeyConstraintType,
}
import { TypeMapping } from '../../helpers';
import { defaultEnumKeyConstraints, defaultEnumValueConstraints } from './constrainer/EnumConstrainer';
import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer';
import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer';
import { GoRenderer } from './GoRenderer';

export const GoDefaultTypeMapping: TypeMapping<GoRenderer> = {
Object ({constrainedModel}): string {
return constrainedModel.name;
},
Reference ({constrainedModel}): string {
return constrainedModel.name;
},
Any (): string {
return 'interface{}';
},
Float (): string {
return 'float64';
},
Integer (): string {
return 'int';
},
String (): string {
return 'string';
},
Boolean (): string {
return 'bool';
},
Tuple (): string {
//Because Java have no notion of tuples (and no custom implementation), we have to render it as a list of any value.
return '[]interface{}';
},
Array ({constrainedModel}): string {
return `[]${constrainedModel.valueModel.type}`;
},
Enum ({constrainedModel}): string {
return constrainedModel.name;
},
Union (): string {
//Because Java have no notion of unions (and no custom implementation), we have to render it as any value.
return 'interface{}';
},
Dictionary ({constrainedModel}): string {
return `map[${constrainedModel.key.type}]${constrainedModel.value.type}`;
}
};

export const DefaultGoConstraints: GoConstraints = {
export const GoDefaultConstraints = {
enumKey: defaultEnumKeyConstraints(),
enumValue: defaultEnumValueConstraints(),
modelName: defaultModelNameConstraints(),
propertyKey: defaultPropertyKeyConstraints()
};

type TypeMappingFunction<T extends ConstrainedMetaModel> = (model: T) => string;

type TypeMapping = {
Object?: TypeMappingFunction<ConstrainedObjectModel>,
Reference?: TypeMappingFunction<ConstrainedReferenceModel>,
Any?: TypeMappingFunction<ConstrainedAnyModel>,
Float?: TypeMappingFunction<ConstrainedFloatModel>,
Integer?: TypeMappingFunction<ConstrainedIntegerModel>,
String?: TypeMappingFunction<ConstrainedStringModel>,
Boolean?: TypeMappingFunction<ConstrainedBooleanModel>,
Tuple?: TypeMappingFunction<ConstrainedTupleModel>,
Array?: TypeMappingFunction<ConstrainedArrayModel>,
Enum?: TypeMappingFunction<ConstrainedEnumModel>,
Union?: TypeMappingFunction<ConstrainedUnionModel>,
Dictionary?: TypeMappingFunction<ConstrainedDictionaryModel>
}

function constrainReferenceModel(constrainedName: string, metaModel: ReferenceModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedReferenceModel {
const constrainedRefModel = constrainMetaModel(metaModel.ref, typeMapping, constrainRules);
const constrainedModel = new ConstrainedReferenceModel(constrainedName, metaModel.originalInput, '', constrainedRefModel);
if (typeMapping.Reference !== undefined) {
constrainedModel.type = typeMapping.Reference(constrainedModel);
} else {
constrainedModel.type = constrainedModel.name;
}
return constrainedModel;
}
function constrainAnyModel(constrainedName: string, metaModel: AnyModel, typeMapping: TypeMapping): ConstrainedAnyModel {
const constrainedModel = new ConstrainedAnyModel(constrainedName, metaModel.originalInput, '');
if (typeMapping.Any !== undefined) {
constrainedModel.type = typeMapping.Any(constrainedModel);
} else {
constrainedModel.type = 'interface{}';
}
return constrainedModel;
}
function constrainFloatModel(constrainedName: string, metaModel: FloatModel, typeMapping: TypeMapping): ConstrainedFloatModel {
const constrainedModel = new ConstrainedFloatModel(constrainedName, metaModel.originalInput, '');
if (typeMapping.Float !== undefined) {
constrainedModel.type = typeMapping.Float(constrainedModel);
} else {
constrainedModel.type = 'float64';
}
return constrainedModel;
}
function constrainIntegerModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
const constrainedModel = new ConstrainedIntegerModel(constrainedName, metaModel.originalInput, '');
if (typeMapping.Integer !== undefined) {
constrainedModel.type = typeMapping.Integer(constrainedModel);
} else {
constrainedModel.type = 'int';
}
return constrainedModel;
}
function constrainStringModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
const constrainedModel = new ConstrainedStringModel(constrainedName, metaModel.originalInput, '');
if (typeMapping.String !== undefined) {
constrainedModel.type = typeMapping.String(constrainedModel);
} else {
constrainedModel.type = 'string';
}
return constrainedModel;
}
function constrainBooleanModel(constrainedName: string, metaModel: BooleanModel, typeMapping: TypeMapping): ConstrainedBooleanModel {
const constrainedModel = new ConstrainedBooleanModel(constrainedName, metaModel.originalInput, '');
if (typeMapping.Boolean !== undefined) {
constrainedModel.type = typeMapping.Boolean(constrainedModel);
} else {
constrainedModel.type = 'bool';
}
return constrainedModel;
}
function constrainTupleModel(constrainedName: string, metaModel: TupleModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedTupleModel {
const constrainedTupleModels = metaModel.tuple.map((tupleValue) => {
const tupleType = constrainMetaModel(tupleValue.value, typeMapping, constrainRules);
return new ConstrainedTupleValueModel(tupleValue.index, tupleType);
});
const constrainedModel = new ConstrainedTupleModel(constrainedName, metaModel.originalInput, '', constrainedTupleModels);
if (typeMapping.Tuple !== undefined) {
constrainedModel.type = typeMapping.Tuple(constrainedModel);
} else {
const tupleType = '[]interface{}';
constrainedModel.type = tupleType;
}
return constrainedModel;
}
function constrainArrayModel(constrainedName: string, metaModel: ArrayModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedArrayModel {
const constrainedValueModel = constrainMetaModel(metaModel.valueModel, typeMapping, constrainRules);
const constrainedModel = new ConstrainedArrayModel(constrainedName, metaModel.originalInput, '', constrainedValueModel);
if (typeMapping.Array !== undefined) {
constrainedModel.type = typeMapping.Array(constrainedModel);
} else {
constrainedModel.type = `[]${constrainedValueModel.type}`;
}
return constrainedModel;
}
function constrainUnionModel(constrainedName: string, metaModel: UnionModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedUnionModel {
const constrainedUnionModels = metaModel.union.map((unionValue) => {
return constrainMetaModel(unionValue, typeMapping, constrainRules);
});
const constrainedModel = new ConstrainedUnionModel(constrainedName, metaModel.originalInput, '', constrainedUnionModels);
if (typeMapping.Union !== undefined) {
constrainedModel.type = typeMapping.Union(constrainedModel);
} else {
constrainedModel.type = 'interface{}';
}
return constrainedModel;
}
function constrainDictionaryModel(constrainedName: string, metaModel: DictionaryModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedDictionaryModel {
const keyModel = constrainMetaModel(metaModel.key, typeMapping, constrainRules);
const valueModel = constrainMetaModel(metaModel.value, typeMapping, constrainRules);
const constrainedModel = new ConstrainedDictionaryModel(constrainedName, metaModel.originalInput, '', keyModel, valueModel, metaModel.serializationType);
if (typeMapping.Dictionary !== undefined) {
constrainedModel.type = typeMapping.Dictionary(constrainedModel);
} else {
const type = `map[${keyModel.type}]${valueModel.type}`;
constrainedModel.type = type;
}
return constrainedModel;
}

function constrainObjectModel(constrainedName: string, objectModel: ObjectModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedObjectModel {
const constrainedObjectModel = new ConstrainedObjectModel(constrainedName, objectModel.originalInput, '', {});
for (const [propertyKey, propertyMetaModel] of Object.entries(objectModel.properties)) {
const constrainedPropertyName = constrainRules.propertyKey({propertyKey, constrainedObjectModel, objectModel});
const constrainedProperty = constrainMetaModel(propertyMetaModel, typeMapping, constrainRules);
constrainedObjectModel.properties[String(constrainedPropertyName)] = constrainedProperty;
}
if (typeMapping.Object !== undefined) {
constrainedObjectModel.type = typeMapping.Object(constrainedObjectModel);
} else {
constrainedObjectModel.type = constrainedName;
}
return constrainedObjectModel;
}

export function ConstrainEnumModel(constrainedName: string, enumModel: EnumModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedEnumModel {
const constrainedModel = new ConstrainedEnumModel(constrainedName, enumModel.originalInput, '', []);

for (const enumValue of enumModel.values) {
const constrainedEnumKey = constrainRules.enumKey({enumKey: String(enumValue.key), enumModel, constrainedEnumModel: constrainedModel});
const constrainedEnumValueModel = new ConstrainedEnumValueModel(constrainedEnumKey, enumValue.value);
constrainedModel.values.push(constrainedEnumValueModel);
}
if (typeMapping.Enum !== undefined) {
constrainedModel.type = typeMapping.Enum(constrainedModel);
} else {
constrainedModel.type = constrainedName;
}
return constrainedModel;
}

export function constrainMetaModel(metaModel: MetaModel, typeMapping: TypeMapping, constrainRules: GoConstraints): ConstrainedMetaModel {
const constrainedName = constrainRules.modelName({modelName: metaModel.name});

if (metaModel instanceof ObjectModel) {
return constrainObjectModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof ReferenceModel) {
return constrainReferenceModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof AnyModel) {
return constrainAnyModel(constrainedName, metaModel, typeMapping);
} else if (metaModel instanceof FloatModel) {
return constrainFloatModel(constrainedName, metaModel, typeMapping);
} else if (metaModel instanceof IntegerModel) {
return constrainIntegerModel(constrainedName, metaModel, typeMapping);
} else if (metaModel instanceof StringModel) {
return constrainStringModel(constrainedName, metaModel, typeMapping);
} else if (metaModel instanceof BooleanModel) {
return constrainBooleanModel(constrainedName, metaModel, typeMapping);
} else if (metaModel instanceof TupleModel) {
return constrainTupleModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof ArrayModel) {
return constrainArrayModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof UnionModel) {
return constrainUnionModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof EnumModel) {
return ConstrainEnumModel(constrainedName, metaModel, typeMapping, constrainRules);
} else if (metaModel instanceof DictionaryModel) {
return constrainDictionaryModel(constrainedName, metaModel, typeMapping, constrainRules);
}
throw new Error('Could not constrain model');
}
16 changes: 12 additions & 4 deletions src/generators/go/GoGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {
defaultGeneratorOptions
} from '../AbstractGenerator';
import { CommonModel, CommonInputModel, RenderOutput } from '../../models';
import { TypeHelpers, ModelKind, FormatHelpers } from '../../helpers';
import { TypeHelpers, ModelKind, FormatHelpers, Constraints, TypeMapping } from '../../helpers';
import { GoPreset, GO_DEFAULT_PRESET } from './GoPreset';
import { StructRenderer } from './renderers/StructRenderer';
import { EnumRenderer } from './renderers/EnumRenderer';
import { pascalCaseTransformMerge } from 'change-case';
import { Logger } from '../../utils/LoggingInterface';
import { isReservedGoKeyword } from './Constants';
import { GoDefaultConstraints, GoDefaultTypeMapping } from './GoConstrainer';
import { GoRenderer } from './GoRenderer';
/**
* The Go naming convention type
*/
Expand Down Expand Up @@ -48,6 +50,8 @@ export const GoNamingConventionImplementation: GoNamingConvention = {

export interface GoOptions extends CommonGeneratorOptions<GoPreset> {
namingConvention?: GoNamingConvention;
typeMapping: TypeMapping<GoRenderer>;
constraints: Constraints
}

export interface GoRenderCompleteModelOptions {
Expand All @@ -61,12 +65,16 @@ export class GoGenerator extends AbstractGenerator<GoOptions, GoRenderCompleteMo
static defaultOptions: GoOptions = {
...defaultGeneratorOptions,
defaultPreset: GO_DEFAULT_PRESET,
namingConvention: GoNamingConventionImplementation
namingConvention: GoNamingConventionImplementation,
typeMapping: GoDefaultTypeMapping,
constraints: GoDefaultConstraints
};
constructor(
options: GoOptions = GoGenerator.defaultOptions,
options: Partial<GoOptions> = GoGenerator.defaultOptions,
) {
super('Go', GoGenerator.defaultOptions, options);
const mergedOptions = {...GoGenerator.defaultOptions, ...options};

super('Go', GoGenerator.defaultOptions, mergedOptions);
}
reservedGoKeyword(name: string): boolean {
return isReservedGoKeyword(name);
Expand Down
53 changes: 18 additions & 35 deletions src/generators/go/constrainer/EnumConstrainer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { ConstrainedEnumModel, EnumModel } from '../../../models';
import { NO_NUMBER_START_CHAR, NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE} from '../../../helpers/Constraints';
import { FormatHelpers } from '../../../helpers';
import { NO_NUMBER_START_CHAR, NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints';
import { FormatHelpers, EnumKeyConstraint, EnumValueConstraint } from '../../../helpers';
import { isReservedGoKeyword } from '../Constants';

export type ModelEnumKeyConstraints = {
NO_SPECIAL_CHAR: (value: string) => string;
Expand All @@ -22,44 +22,27 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = {
NO_EMPTY_VALUE,
NAMING_FORMATTER: FormatHelpers.toConstantCase,
NO_RESERVED_KEYWORDS: (value: string) => {
return value;
return NO_RESERVED_KEYWORDS(value, isReservedGoKeyword);
}
};

export type EnumKeyContext = {
enumKey: string,
constrainedEnumModel: ConstrainedEnumModel,
enumModel: EnumModel
}
export type EnumConstraintType = (context: EnumKeyContext, constraints?: ModelEnumKeyConstraints) => string;
export function defaultEnumKeyConstraints(customConstraints?: Partial<ModelEnumKeyConstraints>): EnumKeyConstraint {
const constraints = {...DefaultEnumKeyConstraints, ...customConstraints};
jonaslagoni marked this conversation as resolved.
Show resolved Hide resolved

export function defaultEnumKeyConstraints(customConstraints?: ModelEnumKeyConstraints): EnumConstraintType {
const constraints = DefaultEnumKeyConstraints;
if (customConstraints !== undefined) {
if (customConstraints.NAMING_FORMATTER !== undefined) {
constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
}
if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
}
if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
}
if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
constraints.NO_RESERVED_KEYWORDS = customConstraints.NO_RESERVED_KEYWORDS;
}
if (customConstraints.NO_DUPLICATE_KEYS !== undefined) {
constraints.NO_DUPLICATE_KEYS = customConstraints.NO_DUPLICATE_KEYS;
}
}
return ({enumKey, enumModel, constrainedEnumModel}) => {
let constrainedEnumKey = enumKey;
constrainedEnumKey = constraints.NO_SPECIAL_CHAR!(constrainedEnumKey);
constrainedEnumKey = constraints.NO_NUMBER_START_CHAR!(constrainedEnumKey);
constrainedEnumKey = constraints.NO_EMPTY_VALUE!(constrainedEnumKey);
constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS!(constrainedEnumKey);
constrainedEnumKey = constraints.NAMING_FORMATTER!(constrainedEnumKey);
constrainedEnumKey = constraints.NO_DUPLICATE_KEYS!(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER!);
constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey);
constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey);
constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey);
constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey);
constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey);
constrainedEnumKey = constraints.NO_DUPLICATE_KEYS(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER);
return constrainedEnumKey;
};
}

export function defaultEnumValueConstraints(): EnumValueConstraint {
return ({enumValue}) => {
return `"${enumValue}"`;
};
}
Loading