@autorest/go
Version:
AutoRest Go Generator
188 lines • 8.8 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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