diff --git a/compiler/src/transform/expand-generics.ts b/compiler/src/transform/expand-generics.ts index 065bce4ba6..b69494fec4 100644 --- a/compiler/src/transform/expand-generics.ts +++ b/compiler/src/transform/expand-generics.ts @@ -35,16 +35,42 @@ import { sortTypeDefinitions } from '../model/utils' import { argv } from 'zx' import { join } from 'path' +export class ExpansionConfig { + unwrappedTypes?: TypeName[] | string[] + inlinedTypes?: TypeName[] | string[] +} + /** - * Expand all generics by creating new concrete types for every instanciation of a generic type. + * Expand all generics by creating new concrete types for every instantiation of a generic type. * * The resulting model has no generics anymore. Top-level generic parameters (e.g. SearchRequest's TDocument) are * replaced by user_defined_data. * * @param inputModel the input model + * @param unwrappedTypes types that should not be expanded but unwrapped as their generic parameter. * @return a new model with generics expanded */ -export function expandGenerics (inputModel: Model): Model { +export function expandGenerics (inputModel: Model, config?: ExpansionConfig): Model { + + const typesToUnwrap = new Set() + const typesToInline: Set = new Set() + + for (const name of config?.unwrappedTypes || []) { + if (typeof name === 'string') { + typesToUnwrap.add(name) + } else { + typesToUnwrap.add(nameKey(name)) + } + } + + for (const name of config?.inlinedTypes || []) { + if (typeof name === 'string') { + typesToInline.add(name) + } else { + typesToInline.add(nameKey(name)) + } + } + const typesSeen = new Set() const types = new Array() @@ -306,6 +332,34 @@ export function expandGenerics (inputModel: Model): Model { } case 'instance_of': { + const valueOfType = nameKey(value.type) + + // If this is a type that has to be unwrapped, return its generic parameter's type + if (typesToUnwrap.has(valueOfType)) { + // @ts-ignore + return expandValueOf(value.generics[0], mappings) + } + + // If this is a type that has to be inlined + if (typesToInline.has(valueOfType)) { + // It has to be an alias (e.g. Stringified or WithNullValue + const inlinedTypeDef = inputTypeByName.get(valueOfType) + if (!inlinedTypeDef || inlinedTypeDef.kind !== 'type_alias') { + throw Error(`Inlined type ${valueOfType} should be an alias definition`) + } + + const inlineMappings = new Map() + for (let i = 0; i < (inlinedTypeDef.generics?.length || 0); i++) { + // @ts-ignore + const source = inlinedTypeDef.generics[i] + // @ts-ignore + const dest = value.generics[i]; + inlineMappings.set(nameKey(source), dest) + } + + return expandValueOf(inlinedTypeDef.type, inlineMappings); + } + // If this is a generic parameter, return its mapping const mapping = mappings.get(nameKey(value.type)) if (mapping !== undefined) { @@ -430,7 +484,10 @@ async function expandGenericsFromFile (inPath: string, outPath: string): Promise ) const inputModel = JSON.parse(inputText) - const outputModel = expandGenerics(inputModel) + const outputModel = expandGenerics(inputModel, { + // unwrappedTypes: ["_spec_utils:Stringified"], + inlinedTypes: ["_spec_utils:WithNullValue"] + }) await writeFile( outPath,