@autorest/go
Version:
AutoRest Go Generator
202 lines • 9.11 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 * as helpers from './helpers.js';
import { ImportManager } from './imports.js';
import { CodegenError } from './errors.js';
/**
* Creates the content for the responses.go file.
*
* @param pkg contains the package content
* @param options the emitter options
* @returns the text for the file or the empty string
*/
export function generateResponses(pkg, options) {
if (pkg.responseEnvelopes.length === 0) {
return {
responses: '',
serDe: '',
};
}
const indent = new helpers.Indentation();
const imports = new ImportManager(pkg);
const serdeImports = new ImportManager(pkg);
let responses = helpers.contentPreamble(pkg);
let serDe = '';
let respContent = '';
let serdeContent = '';
for (const respEnv of pkg.responseEnvelopes) {
respContent += emit(respEnv, imports, indent);
if (options.generateFakes) {
serdeContent += generateMarshaller(respEnv, serdeImports, indent);
}
serdeContent += generateUnmarshaller(respEnv, serdeImports, indent);
}
responses += imports.text();
responses += respContent;
if (serdeContent.length > 0) {
serDe = helpers.contentPreamble(pkg);
serDe += serdeImports.text();
serDe += serdeContent;
}
return {
responses: responses,
serDe: serDe,
};
}
/**
* generates a marshaler for the provided response envelope.
* note that this is only required for fakes.
*
* @param respEnv the response envelope for which to create a marshaler
* @param imports the import manager currently in scope
* @param indent the indentation helper currently in scope
* @returns the text for the response envelope's marshaler
*/
function generateMarshaller(respEnv, imports, indent) {
let text = '';
if (go.isLROMethod(respEnv.method) && respEnv.result?.kind === 'polymorphicResult') {
// fakes require a custom marshaller for polymorphics results so that the data is in the correct shape.
// without it, the response envelope type name is the outer type which is incorrect.
imports.add('encoding/json');
const receiver = respEnv.name[0].toLowerCase();
text += `${helpers.comment(`MarshalJSON implements the json.Marshaller interface for type ${respEnv.name}.`, '// ', undefined, helpers.commentLength)}\n`;
text += `func (${receiver} ${respEnv.name}) MarshalJSON() ([]byte, error) {\n`;
// TODO: this doesn't include any headers. however, LROs with header responses are currently broken :(
text += `${indent.get()}return json.Marshal(${receiver}.${go.getTypeDeclaration(respEnv.result.interface, respEnv.method.receiver.type.pkg)})\n}\n\n`;
}
return text;
}
/**
* generates an unmarshaler for the provided response envelope.
* note that not all response envelopes require one.
*
* @param respEnv the response envelope for which to create an unmarshaler
* @param imports the import manager currently in scope
* @param indent the indentation helper currently in scope
* @returns the text for the response envelope's unmarshaler or the empty string
*/
function generateUnmarshaller(respEnv, imports, indent) {
// if the response envelope contains a discriminated type we need an unmarshaller
let polymorphicRes;
// in addition, if it's an LRO operation that returns a scalar, we will also need one
let monomorphicRes;
if (respEnv.result?.kind === 'polymorphicResult') {
polymorphicRes = respEnv.result;
}
else if (go.isLROMethod(respEnv.method) && respEnv.result?.kind === 'monomorphicResult') {
monomorphicRes = respEnv.result;
}
if (!polymorphicRes && !monomorphicRes) {
// no unmarshaller required
return '';
}
const receiver = respEnv.name[0].toLowerCase();
let unmarshaller = `${helpers.comment(`UnmarshalJSON implements the json.Unmarshaller interface for type ${respEnv.name}.`, '// ', undefined, helpers.commentLength)}\n`;
unmarshaller += `func (${receiver} *${respEnv.name}) UnmarshalJSON(data []byte) error {\n`;
// add a custom unmarshaller to the response envelope
if (polymorphicRes) {
const type = polymorphicRes.interface.name;
unmarshaller += `${indent.get()}res, err := unmarshal${type}(data)\n`;
unmarshaller += `${indent.get()}if err != nil {\n`;
indent.push();
unmarshaller += `${indent.get()}return err\n`;
indent.pop();
unmarshaller += `${indent.get()}}\n`;
unmarshaller += `${indent.get()}${receiver}.${type} = res\n`;
unmarshaller += `${indent.get()}return nil\n`;
}
else if (monomorphicRes) {
imports.add('encoding/json');
unmarshaller += `${indent.get()}return json.Unmarshal(data, &${receiver}.${monomorphicRes.fieldName})\n`;
}
else {
throw new CodegenError('InternalError', `unhandled case for response envelope ${respEnv.name}`);
}
unmarshaller += '}\n\n';
return unmarshaller;
}
/**
* emits the type definition for the provided response envelope.
*
* @param pkg the package to contain the response envelope
* @param respEnv the response envelope for which to emit the definition
* @param imports the import manager currently in scope
* @param indent the indentation helper currently in scope
* @returns the text for the response enveloipe type definition
*/
function emit(respEnv, imports, indent) {
let text = helpers.formatDocComment(respEnv.docs);
text += `type ${respEnv.name} struct {\n`;
if (!respEnv.result && respEnv.headers.length === 0) {
// this is an empty response envelope
text += `${indent.get()}// placeholder for future response values\n`;
}
else {
// fields will contain the merged headers and response field so they can be sorted together
const fields = new Array();
// used to track when to add an extra \n between fields that have comments
let first = true;
if (respEnv.result) {
const respType = go.getResultType(respEnv.result);
imports.addForType(respType);
if (respEnv.result.kind === 'modelResult' || respEnv.result.kind === 'polymorphicResult') {
// anonymously embedded type always goes first
text += helpers.formatDocComment(respEnv.result.docs);
text += `${indent.get()}${go.getTypeDeclaration(respType, respEnv.method.receiver.type.pkg)}\n`;
first = false;
}
else {
let tag = '';
if (respEnv.result.kind === 'monomorphicResult' && respEnv.result.format === 'XML') {
// only emit tags for XML; JSON uses custom marshallers/unmarshallers
if (respEnv.result.xml?.wraps) {
tag = ` \`xml:"${respEnv.result.xml.wraps}"\``;
}
else if (respEnv.result.xml?.name) {
tag = ` \`xml:"${respEnv.result.xml.name}"\``;
}
}
let byValue = true;
if (respEnv.result.kind === 'monomorphicResult') {
byValue = respEnv.result.byValue;
}
fields.push({
docs: respEnv.result.docs,
field: `${indent.get()}${respEnv.result.fieldName} ${helpers.star(byValue)}${go.getTypeDeclaration(respType, respEnv.method.receiver.type.pkg)}${tag}\n`,
});
}
}
for (const header of respEnv.headers) {
imports.addForType(header.type);
let byValue = true;
if (header.kind === 'headerScalarResponse') {
byValue = header.byValue;
}
fields.push({
docs: header.docs,
field: `${indent.get()}${header.fieldName} ${helpers.star(byValue)}${go.getTypeDeclaration(header.type, respEnv.method.receiver.type.pkg)}\n`,
});
}
fields.sort((a, b) => {
return helpers.sortAscending(a.field, b.field);
});
for (const field of fields) {
if (field.docs.summary || field.docs.description) {
if (!first) {
// add an extra new-line between fields IFF the field
// has a comment and it's not the very first one.
text += '\n';
}
text += helpers.formatDocComment(field.docs);
}
text += field.field;
first = false;
}
}
text += '}\n\n';
return text;
}
//# sourceMappingURL=responses.js.map