@autorest/go
Version:
AutoRest Go Generator
173 lines • 8.16 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 { values } from '@azure-tools/linq';
import * as helpers from './helpers.js';
import { ImportManager } from './imports.js';
// Creates the content in polymorphic_helpers.go
export async function generatePolymorphicHelpers(codeModel, fakeServerPkg) {
if (codeModel.interfaces.length === 0) {
// no polymorphic types
return '';
}
let text = helpers.contentPreamble(codeModel, true, fakeServerPkg);
const imports = new ImportManager();
imports.add('encoding/json');
if (fakeServerPkg) {
// content is being generated into a separate package, add the necessary import
imports.add(helpers.getParentImport(codeModel));
}
text += imports.text();
const scalars = new Set();
const arrays = new Set();
const maps = new Set();
// we know there are polymorphic types but we don't know how they're used.
// i.e. are they vanilla fields, elements in a slice, or values in a map.
// polymorphic types within maps/slices will also need the scalar helpers.
const trackDisciminator = function (type) {
switch (type.kind) {
case 'interface':
scalars.add(type.name);
break;
case 'map': {
const leafType = helpers.recursiveUnwrapMapSlice(type);
if (leafType.kind === 'interface') {
scalars.add(leafType.name);
maps.add(leafType.name);
}
break;
}
case 'slice': {
const leafType = helpers.recursiveUnwrapMapSlice(type);
if (leafType.kind === 'interface') {
scalars.add(leafType.name);
arrays.add(leafType.name);
}
break;
}
}
};
// calculate which discriminator helpers we actually need to generate
if (fakeServerPkg) {
// when generating for the fakes server, we must look at operation parameters instead of return values
for (const client of values(codeModel.clients)) {
for (const method of values(client.methods)) {
for (const param of values(method.parameters)) {
trackDisciminator(param.type);
}
}
}
}
else {
for (const model of codeModel.models) {
for (const field of model.fields) {
trackDisciminator(field.type);
}
}
for (const respEnv of values(codeModel.responseEnvelopes)) {
switch (respEnv.result?.kind) {
case 'monomorphicResult':
switch (respEnv.result.monomorphicType.kind) {
case 'map':
trackDisciminator(respEnv.result.monomorphicType.valueType);
break;
case 'slice':
trackDisciminator(respEnv.result.monomorphicType.elementType);
break;
}
break;
case 'polymorphicResult':
trackDisciminator(respEnv.result.interface);
break;
}
}
}
if (scalars.size === 0 && arrays.size === 0 && maps.size === 0) {
// this is a corner-case that can happen when all the discriminated types
// are error types. there's a bug in M4 that incorrectly annotates such
// types as 'output', 'exception' in the usage however it's really just
// 'exception'. until this is fixed, we can wind up here.
return '';
}
let prefix = '';
if (fakeServerPkg) {
// content is being generated into a separate package, set the type name prefix
prefix = `${codeModel.packageName}.`;
}
for (const interfaceType of codeModel.interfaces) {
// generate unmarshallers for each discriminator
// scalar unmarshaller
if (scalars.has(interfaceType.name)) {
text += `func unmarshal${interfaceType.name}(rawMsg json.RawMessage) (${prefix}${interfaceType.name}, error) {\n`;
text += '\tif rawMsg == nil || string(rawMsg) == "null" {\n';
text += '\t\treturn nil, nil\n';
text += '\t}\n';
text += '\tvar m map[string]any\n';
text += '\tif err := json.Unmarshal(rawMsg, &m); err != nil {\n';
text += '\t\treturn nil, err\n';
text += '\t}\n';
text += `\tvar b ${prefix}${interfaceType.name}\n`;
text += `\tswitch m["${interfaceType.discriminatorField}"] {\n`;
for (const possibleType of interfaceType.possibleTypes) {
let disc = helpers.formatLiteralValue(possibleType.discriminatorValue, true);
// when the discriminator value is an enum, cast the const as a string
if (possibleType.discriminatorValue.type.kind === 'constant') {
disc = `string(${prefix}${disc})`;
}
text += `\tcase ${disc}:\n`;
text += `\t\tb = &${prefix}${possibleType.name}{}\n`;
}
text += '\tdefault:\n';
text += `\t\tb = &${prefix}${interfaceType.rootType.name}{}\n`;
text += '\t}\n';
text += '\tif err := json.Unmarshal(rawMsg, b); err != nil {\n\t\treturn nil, err\n\t}\n';
text += '\treturn b, nil\n';
text += '}\n\n';
}
// array unmarshaller
if (arrays.has(interfaceType.name)) {
text += `func unmarshal${interfaceType.name}Array(rawMsg json.RawMessage) ([]${prefix}${interfaceType.name}, error) {\n`;
text += '\tif rawMsg == nil || string(rawMsg) == "null" {\n';
text += '\t\treturn nil, nil\n';
text += '\t}\n';
text += '\tvar rawMessages []json.RawMessage\n';
text += '\tif err := json.Unmarshal(rawMsg, &rawMessages); err != nil {\n';
text += '\t\treturn nil, err\n';
text += '\t}\n';
text += `\tfArray := make([]${prefix}${interfaceType.name}, len(rawMessages))\n`;
text += '\tfor index, rawMessage := range rawMessages {\n';
text += `\t\tf, err := unmarshal${interfaceType.name}(rawMessage)\n`;
text += '\t\tif err != nil {\n';
text += '\t\t\treturn nil, err\n';
text += '\t\t}\n';
text += '\t\tfArray[index] = f\n';
text += '\t}\n';
text += '\treturn fArray, nil\n';
text += '}\n\n';
}
// map unmarshaller
if (maps.has(interfaceType.name)) {
text += `func unmarshal${interfaceType.name}Map(rawMsg json.RawMessage) (map[string]${prefix}${interfaceType.name}, error) {\n`;
text += '\tif rawMsg == nil || string(rawMsg) == "null" {\n';
text += '\t\treturn nil, nil\n';
text += '\t}\n';
text += '\tvar rawMessages map[string]json.RawMessage\n';
text += '\tif err := json.Unmarshal(rawMsg, &rawMessages); err != nil {\n';
text += '\t\treturn nil, err\n';
text += '\t}\n';
text += `\tfMap := make(map[string]${prefix}${interfaceType.name}, len(rawMessages))\n`;
text += '\tfor key, rawMessage := range rawMessages {\n';
text += `\t\tf, err := unmarshal${interfaceType.name}(rawMessage)\n`;
text += '\t\tif err != nil {\n';
text += '\t\t\treturn nil, err\n';
text += '\t\t}\n';
text += '\t\tfMap[key] = f\n';
text += '\t}\n';
text += '\treturn fMap, nil\n';
text += '}\n\n';
}
}
return text;
}
//# sourceMappingURL=polymorphics.js.map