UNPKG

@autorest/go

Version:
188 lines 8.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ /* eslint-disable @typescript-eslint/no-unsafe-argument */ import * as m4 from '@autorest/codemodel'; import { serialize } from '@azure-tools/codegen'; import { values } from '@azure-tools/linq'; import { startSession } from '@autorest/extension-base'; import * as go from '../../../codemodel.go/src/index.js'; import { adaptClients } from './clients.js'; import { adaptConstantType, adaptInterfaceType, adaptModel, adaptModelField } from './types.js'; import { aggregateProperties } from '../transform/helpers.js'; import { fileURLToPath } from 'url'; // converts an M4 code model into a GoCodeModel export async function m4ToGoCodeModel(host) { const debug = (await host.getValue('debug')) || false; try { const session = await startSession(host, m4.codeModelSchema); const info = new go.Info(session.model.info.title); const options = new go.Options(await session.getValue('generate-fakes', session.model.language.go.azureARM), await session.getValue('inject-spans', session.model.language.go.azureARM), await session.getValue('disallow-unknown-fields', false), await session.getValue('generate-sdk-example', false)); options.headerText = await session.getValue('header-text', 'MISSING LICENSE HEADER'); options.factoryGatherAllParams = await session.getValue('factory-gather-all-params', true); options.omitConstructors = true; const azcoreVersion = await session.getValue('azcore-version', ''); if (azcoreVersion !== '') { options.azcoreVersion = azcoreVersion; } let type = 'data-plane'; if (session.model.language.go.azureARM) { type = 'azure-arm'; } let root; if (session.model.language.go.module) { root = new go.Module(session.model.language.go.module); } else if (session.model.language.go.containingModule !== '') { root = new go.ContainingModule(session.model.language.go.containingModule); root.package = new go.Package(session.model.language.go.packageName, root); } else { // this was validated earlier but is required // to avoid root from being unassigned. throw new Error('missing module and containing-module'); } const codeModel = new go.CodeModel(info, type, options, root); // since we only support single packages in autorest, the root will // either be the package in the containing module or the module itself. const pkg = codeModel.root.kind === 'containingModule' ? codeModel.root.package : codeModel.root; adaptConstantTypes(session.model, pkg); adaptInterfaceTypes(session.model, pkg); adaptModels(session.model, pkg); adaptClients(session.model, codeModel); const paramGroups = new Map(); for (const client of values(pkg.clients)) { for (const method of client.methods) { pkg.responseEnvelopes.push(method.returns); for (const param of values(method.parameters)) { if (param.group) { if (!paramGroups.has(param.group.groupName)) { paramGroups.set(param.group.groupName, param.group); } } } if (!paramGroups.has(method.optionalParamsGroup.groupName)) { // the optional params group wasn't present, that means that it's empty. paramGroups.set(method.optionalParamsGroup.groupName, method.optionalParamsGroup); } } } if (paramGroups.size > 0) { // adapt all of the parameter groups for (const groupName of paramGroups.keys()) { const paramGroup = paramGroups.get(groupName); pkg.paramGroups.push(adaptParameterGroup(pkg, paramGroup)); } } // output the model to the pipeline host.writeFile({ filename: 'go-code-model.yaml', content: serialize(codeModel), artifactType: 'go-code-model', }); } catch (E) { if (debug) { console.error(`${fileURLToPath(import.meta.url)} - FAILURE ${JSON.stringify(E)} ${E.stack}`); } throw E; } } function adaptConstantTypes(m4CodeModel, pkg) { // group all enum categories into a single array so they can be sorted for (const choice of values(m4CodeModel.schemas.choices)) { if (choice.language.go.omitType) { continue; } const constType = adaptConstantType(choice, pkg); pkg.constants.push(constType); } for (const choice of values(m4CodeModel.schemas.sealedChoices)) { if (choice.language.go.omitType || choice.choices.length === 1) { continue; } const constType = adaptConstantType(choice, pkg); pkg.constants.push(constType); } } function adaptParameterGroup(pkg, paramGroup) { const structType = new go.Struct(pkg, paramGroup.groupName); structType.docs = paramGroup.docs; if (paramGroup.params.length > 0) { for (const param of values(paramGroup.params)) { if (param.style === 'literal') { continue; } let byValue = param.style === 'required' || (param.location === 'client' && go.isClientSideDefault(param.style)); // if the param isn't required, check if it should be passed by value or not. // optional params that are implicitly nil-able shouldn't be pointer-to-type. if (!byValue) { byValue = param.byValue; } const field = new go.StructField(param.name, param.type, byValue); field.docs = param.docs; structType.fields.push(field); } } return structType; } function adaptInterfaceTypes(m4CodeModel, pkg) { if (!m4CodeModel.language.go.discriminators) { return; } const ifaceObjs = new Array(); const discriminators = m4CodeModel.language.go.discriminators; // discriminators contains all of the root discriminated types but *not* any sub-roots (e.g. Salmon). for (const discriminator of values(discriminators)) { if (discriminator.language.go.omitType || discriminator.extensions?.['x-ms-external']) { continue; } // we must adapt all InterfaceTypes first. this is because ModelTypes/PolymorphicTypes can // contain references to InterfaceTypes and/or cyclic references recursiveAdaptInterfaceType(discriminator, pkg.interfaces, ifaceObjs, pkg); } // now that the InterfaceTypes have been created, we can populate the rootType and possibleTypes for (const ifaceObj of values(ifaceObjs)) { ifaceObj.iface.rootType = adaptModel(ifaceObj.obj, pkg); ifaceObj.iface.possibleTypes = new Array(); for (const disc of values(ifaceObj.obj.discriminator.all)) { const possibleType = adaptModel(disc, pkg); ifaceObj.iface.possibleTypes.push(possibleType); } } } function recursiveAdaptInterfaceType(obj, ifaces, ifaceObjs, pkg, parent) { const iface = adaptInterfaceType(obj, pkg, parent); if (ifaces.includes(iface)) { return; } ifaces.push(iface); ifaceObjs.push({ iface, obj }); for (const val of values(obj.discriminator.immediate)) { const asObj = val; if (asObj.discriminator) { recursiveAdaptInterfaceType(asObj, ifaces, ifaceObjs, pkg, iface); } } } function adaptModels(m4CodeModel, pkg) { const modelObjs = new Array(); for (const obj of values(m4CodeModel.schemas.objects)) { if (obj.language.go.omitType || obj.extensions?.['x-ms-external']) { continue; } // we must adapt all model types first. this is because models can contain cyclic references const modelType = adaptModel(obj, pkg); pkg.models.push(modelType); modelObjs.push({ type: modelType, obj: obj }); } for (const modelObj of values(modelObjs)) { const props = aggregateProperties(modelObj.obj); for (const prop of values(props)) { const field = adaptModelField(prop, modelObj.obj, pkg); modelObj.type.fields.push(field); } } } //# sourceMappingURL=adapter.js.map