@autorest/go
Version:
AutoRest Go Generator
311 lines • 13.1 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as go from '../../codemodel.go/src/index.js';
import { generateClientFactory } from './core/clientFactory.js';
import { generateCloudConfig } from './core/cloudConfig.js';
import { generateConstants } from './core/constants.js';
import { generateExamples } from './core/example.js';
import { generateGoModFile } from './core/gomod.js';
import { setCustomHeaderText } from './core/helpers.js';
import { generateInterfaces } from './core/interfaces.js';
import { generateLicenseTxt } from './core/license.js';
import { generateMetadataFile } from './core/metadata.js';
import { generateModels } from './core/models.js';
import { generateOperations } from './core/operations.js';
import { generateOptions } from './core/options.js';
import { generatePolymorphicHelpers } from './core/polymorphics.js';
import { generateResponses } from './core/responses.js';
import { generateVersionInfo } from './core/version.js';
import { generateXMLAdditionalPropsHelpers } from './core/xmlAdditionalProps.js';
import { generateServers } from './fake/servers.js';
import { generateServerFactory } from './fake/factory.js';
/** used to write a Go code model to disk */
export class Emitter {
codeModel;
fs;
filePrefix;
constructor(codeModel, fs, options) {
this.codeModel = codeModel;
this.fs = fs;
if (options?.filePrefix) {
this.filePrefix = options?.filePrefix;
}
else {
this.filePrefix = '';
}
if (this.codeModel.options.headerText) {
setCustomHeaderText(this.codeModel.options.headerText);
}
switch (this.codeModel.root.kind) {
case 'containingModule':
sortContent(this.codeModel.root.package);
break;
case 'module':
sortContent(this.codeModel.root);
break;
default:
this.codeModel.root;
}
}
/**
* writes the core code model content.
* this content is common to both emitters.
*/
async emit() {
if (this.codeModel.root.kind === 'module') {
// don't overwrite an existing go.mod file, update it if required
const goModFile = 'go.mod';
let existingGoMod;
if (await this.fs.exists(goModFile)) {
existingGoMod = await this.fs.read(goModFile);
}
const gomod = generateGoModFile(this.codeModel.root, this.codeModel.options, existingGoMod);
if (gomod.length > 0) {
await this.fs.write(goModFile, gomod);
}
}
await this.recursiveEmit(async (pkg, write) => {
const clientFactory = generateClientFactory(pkg, this.codeModel.type, this.codeModel.options);
if (clientFactory.length > 0) {
await write('client_factory.go', clientFactory);
}
const constants = generateConstants(pkg);
if (constants.length > 0) {
await write('constants.go', constants);
}
const interfaces = generateInterfaces(pkg);
if (interfaces.length > 0) {
await write('interfaces.go', interfaces);
}
const operations = generateOperations(pkg, this.codeModel.type, this.codeModel.options);
for (const op of operations) {
await write(`${snakeClientFileName(op.name)}.go`, op.content);
}
const models = generateModels(pkg, this.codeModel.options);
if (models.models.length > 0) {
await write('models.go', models.models);
}
if (models.serDe.length > 0) {
await write('models_serde.go', models.serDe);
}
const options = generateOptions(pkg);
if (options.length > 0) {
await write('options.go', options);
}
const polymorphics = generatePolymorphicHelpers(pkg);
if (polymorphics.length > 0) {
await write('polymorphic_helpers.go', polymorphics);
}
const responses = generateResponses(pkg, this.codeModel.options);
if (responses.responses.length > 0) {
await write('responses.go', responses.responses);
}
if (responses.serDe.length > 0) {
await write('responses_serde.go', responses.serDe);
}
const xmlAddlProps = generateXMLAdditionalPropsHelpers(pkg);
if (xmlAddlProps.length > 0) {
await write('xml_helper.go', xmlAddlProps);
}
if (this.codeModel.options.generateFakes) {
const fakePkg = new go.FakePackage(pkg);
const serverContent = generateServers(fakePkg, this.codeModel.type);
if (serverContent.servers.length > 0) {
for (const op of serverContent.servers) {
const fileName = `${snakeClientFileName(op.name, 'server')}.go`;
await write(fileName, op.content, fakePkg.kind);
}
const serverFactory = generateServerFactory(fakePkg, this.codeModel.type);
if (serverFactory.length > 0) {
await write('server_factory.go', serverFactory, fakePkg.kind);
}
await write('internal.go', serverContent.internals, fakePkg.kind);
const polymorphics = generatePolymorphicHelpers(fakePkg);
if (polymorphics.length > 0) {
await write('polymorphic_helpers.go', polymorphics, fakePkg.kind);
}
}
}
});
// only one version.go file per module
if (this.codeModel.root.kind === 'module') {
// don't overwrite an existing version.go file
const versionGo = generateVersionInfo(this.codeModel.root);
const versionGoFileName = `${this.filePrefix}version.go`;
if (versionGo.length > 0 && !(await this.fs.exists(versionGoFileName))) {
await this.fs.write(versionGoFileName, versionGo);
}
}
}
/** writes the cloud_config.go file */
async emitCloudConfig() {
if (this.codeModel.root.kind !== 'module') {
return;
}
const cloudConfig = generateCloudConfig(this.codeModel.root, this.codeModel.type);
if (cloudConfig.length > 0) {
await this.fs.write(`${this.filePrefix}cloud_config.go`, cloudConfig);
}
}
/** writes the *_example_test.go files */
async emitExamples() {
if (!this.codeModel.options.generateExamples) {
return;
}
await this.recursiveEmit(async (pkg, write) => {
const examples = generateExamples(new go.TestPackage(pkg), this.codeModel.type, this.codeModel.options);
for (const example of examples) {
await write(`${snakeClientFileName(example.name)}_example_test.go`, example.content);
}
});
}
/** writes the LICENSE.txt file */
async emitLicenseFile() {
if (this.codeModel.root.kind !== 'module') {
return;
}
// don't overwrite an existing LICENSE.txt file
const licenseTxt = generateLicenseTxt(this.codeModel.options);
const licenseTxtFileName = 'LICENSE.txt';
if (licenseTxt && !(await this.fs.exists(licenseTxtFileName))) {
await this.fs.write(licenseTxtFileName, licenseTxt);
}
}
/** writes the emitter metadata file */
async emitMetadataFile() {
if (this.codeModel.root.kind !== 'module') {
return;
}
const metadata = generateMetadataFile(this.codeModel);
if (metadata.length > 0) {
await this.fs.write('testdata/_metadata.json', metadata);
}
}
/**
* recursively emits package contents.
*
* @param emitForPkg the package contents to emit
*/
async recursiveEmit(emitForPkg) {
const recursiveEmit = async (pkg, dir) => {
await emitForPkg(pkg, async (name, content, subdir) => {
return await this.fs.write(`${dir}${subdir ? `${subdir}/` : ''}${this.filePrefix}${name}`, content);
});
// recursively emit any sub-packages
for (const subPkg of pkg.packages) {
await recursiveEmit(subPkg, `${dir}${subPkg.name}/`);
}
};
switch (this.codeModel.root.kind) {
case 'containingModule':
await recursiveEmit(this.codeModel.root.package, '');
break;
case 'module':
// when emitting a module, the root directory is the module directory
await recursiveEmit(this.codeModel.root, '');
break;
default:
this.codeModel.root;
}
}
}
/**
* creates a snake cased client file name from the provided client name.
* the returned name will not contain the .go suffix.
*
* @param clientName the name of the client (e.g. FooClient)
* @param suffix the client name suffix. the default value is 'client'
* @returns a snaked client file name prefix (e.g. foo_client)
*/
function snakeClientFileName(clientName, suffix = 'client') {
clientName = clientName.toLowerCase();
// fileName is the client name, e.g. FooClient.
// insert a _ before Client, i.e. Foo_Client
// if the name isn't simply Client.
if (clientName !== suffix) {
clientName = `${clientName.substring(0, clientName.length - suffix.length)}_${suffix}`;
}
return clientName;
}
/**
* recursively sorts code model contents by name in alphabetical order.
*
* @param pkg the contents to sort
*/
function sortContent(pkg) {
const sortAscending = function (a, b) {
return a < b ? -1 : a > b ? 1 : 0;
};
pkg.constants.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const enm of pkg.constants) {
enm.values.sort((a, b) => {
return sortAscending(a.name, b.name);
});
}
pkg.interfaces.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const iface of pkg.interfaces) {
// we sort by literal value so that the switch/case statements in polymorphic_helpers.go
// are ordered by the literal value which can be somewhat different from the model name.
// when constant values are used, we sort by the Go identifier instead.
iface.possibleTypes.sort((a, b) => {
const aValue = a.discriminatorValue.type.kind === 'constant' ? a.discriminatorValue.literal.name : a.discriminatorValue.literal;
const bValue = b.discriminatorValue.type.kind === 'constant' ? b.discriminatorValue.literal.name : b.discriminatorValue.literal;
return sortAscending(aValue, bValue);
});
}
pkg.models.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const model of pkg.models) {
model.fields.sort((a, b) => {
return sortAscending(a.name, b.name);
});
}
pkg.paramGroups.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const paramGroup of pkg.paramGroups) {
paramGroup.fields.sort((a, b) => {
return sortAscending(a.name, b.name);
});
}
pkg.responseEnvelopes.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const respEnv of pkg.responseEnvelopes) {
respEnv.headers.sort((a, b) => {
return sortAscending(a.fieldName, b.fieldName);
});
}
pkg.clients.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const client of pkg.clients) {
if (client.instance?.kind === 'constructable') {
client.instance.constructors.sort((a, b) => sortAscending(a.name, b.name));
if (client.instance.options.kind === 'clientOptions') {
client.instance.options.parameters.sort((a, b) => sortAscending(a.name, b.name));
}
}
client.parameters.sort((a, b) => sortAscending(a.name, b.name));
client.methods.sort((a, b) => {
return sortAscending(a.name, b.name);
});
client.clientAccessors.sort((a, b) => {
return sortAscending(a.name, b.name);
});
for (const method of client.methods) {
method.httpStatusCodes.sort();
}
}
for (const subPkg of pkg.packages) {
sortContent(subPkg);
}
}
//# sourceMappingURL=emitter.js.map