UNPKG

@autorest/go

Version:
202 lines 9.11 kB
/*--------------------------------------------------------------------------------------------- * 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