@autorest/go
Version:
AutoRest Go Generator
914 lines • 33.5 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 { values } from '@azure-tools/linq';
import { capitalize, comment, uncapitalize } from '@azure-tools/codegen';
import { CodegenError } from './errors.js';
// variable to be used to determine comment length when calling comment from @azure-tools
export const commentLength = 120;
export const dateFormat = '2006-01-02';
export const datetimeRFC3339Format = 'time.RFC3339Nano';
export const datetimeRFC1123Format = 'time.RFC1123';
export const timeRFC3339Format = '15:04:05.999999999Z07:00';
/**
* returns the common source-file preamble (license comment, package name etc)
*
* @param codeModel the code model being emitted
* @param doNotEdit when false the 'DO NOT EDIT' clause is omitted
* @param packageName overrides the package name in the code model
* @returns the source file preamble
*/
export function contentPreamble(codeModel, doNotEdit = true, packageName) {
if (!packageName) {
packageName = codeModel.packageName;
}
const headerText = comment(codeModel.options.headerText, '// ');
let text = headerText;
if (doNotEdit) {
// ensure tools recognize the file as generated according to
// https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source
text = text.replace(/^\/\/ Code generated .*\.$/m, '$& DO NOT EDIT.');
if (!text.match(/^\/\/ Code generated .* DO NOT EDIT\.$/m)) {
text += '\n// Code generated by @autorest/go. DO NOT EDIT.';
}
}
else {
// remove the blurb about the changes being lost
text = text.replace(/^\/\/ Changes may cause incorrect behavior and will be lost if the code is regenerated\.$/m, '');
}
text += `\n\npackage ${packageName}\n\n`;
return text;
}
// used to sort strings in ascending order
export function sortAscending(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
// returns the type name with possible * prefix
export function formatParameterTypeName(param, pkgName) {
let typeName;
switch (param.kind) {
case 'paramGroup':
typeName = param.groupName;
if (pkgName) {
typeName = `${pkgName}.${typeName}`;
}
if (param.required) {
return typeName;
}
break;
default:
typeName = go.getTypeDeclaration(param.type, pkgName);
if (parameterByValue(param)) {
// client parameters with default values aren't emitted as pointer-to-type
return typeName;
}
}
return `*${typeName}`;
}
export function parameterByValue(param) {
return go.isRequiredParameter(param) || (param.location === 'client' && go.isClientSideDefault(param.style));
}
// sorts parameters by their required state, ordering required before optional
export function sortParametersByRequired(a, b) {
let aRequired = false;
let bRequired = false;
switch (a.kind) {
case 'paramGroup':
aRequired = a.required;
break;
default:
aRequired = go.isRequiredParameter(a);
break;
}
switch (b.kind) {
case 'paramGroup':
bRequired = b.required;
break;
default:
bRequired = go.isRequiredParameter(b);
break;
}
if (aRequired === bRequired) {
return 0;
}
else if (aRequired && !bRequired) {
return -1;
}
return 1;
}
// returns the parameters for the internal request creator method.
// e.g. "i int, s string"
export function getCreateRequestParametersSig(method) {
const methodParams = getMethodParameters(method);
const params = new Array();
params.push('ctx context.Context');
for (const methodParam of values(methodParams)) {
let paramName = uncapitalize(methodParam.name);
// when creating the method sig for fooCreateRequest, if the options type is empty
// or only contains the ResumeToken param use _ for the param name to quiet the linter
if (methodParam.kind === 'paramGroup' && (methodParam.params.length === 0 || (methodParam.params.length === 1 && methodParam.params[0].kind === 'resumeTokenParam'))) {
paramName = '_';
}
params.push(`${paramName} ${formatParameterTypeName(methodParam)}`);
}
return params.join(', ');
}
// returns the parameter names for an operation (excludes the param types).
// e.g. "i, s"
export function getCreateRequestParameters(method) {
// NOTE: keep in sync with getCreateRequestParametersSig
const methodParams = getMethodParameters(method);
const params = new Array();
params.push('ctx');
for (const methodParam of values(methodParams)) {
params.push(uncapitalize(methodParam.name));
}
return params.join(', ');
}
// returns the complete collection of method parameters
export function getMethodParameters(method, paramsFilter) {
const params = new Array();
const paramGroups = new Array();
let methodParams = method.parameters;
if (paramsFilter) {
methodParams = paramsFilter(methodParams);
}
for (const param of values(methodParams)) {
if (param.location === 'client') {
// client params are passed via the receiver
// must check before param group as client params can be grouped
continue;
}
else if (param.group) {
// param groups will be added after individual params
if (!paramGroups.includes(param.group)) {
paramGroups.push(param.group);
}
}
else if (param.type.kind === 'literal') {
// don't generate a parameter for a constant
// NOTE: this check must come last as non-required optional constants
// in header/query params get dumped into the optional params group
continue;
}
else {
params.push(param);
}
}
// move global optional params to the end of the slice
params.sort(sortParametersByRequired);
// add any parameter groups. optional groups go last
paramGroups.sort((a, b) => {
if (a.required === b.required) {
return 0;
}
if (a.required && !b.required) {
return -1;
}
return 1;
});
// add the optional param group last if it's not already in the list.
if (method.kind !== 'nextPageMethod') {
if (!values(paramGroups).any(gp => { return gp.groupName === method.optionalParamsGroup.groupName; })) {
paramGroups.push(method.optionalParamsGroup);
}
}
const combined = new Array();
for (const param of params) {
combined.push(param);
}
for (const paramGroup of paramGroups) {
combined.push(paramGroup);
}
return combined;
}
// returns the fully-qualified parameter name. this is usually just the name
// but will include the client or optional param group name prefix as required.
export function getParamName(param) {
let paramName = param.name;
// must check paramGroup first as client params can also be grouped
if (param.group) {
paramName = `${uncapitalize(param.group.name)}.${capitalize(paramName)}`;
}
if (param.location === 'client') {
paramName = `client.${paramName}`;
}
// client parameters with default values aren't emitted as pointer-to-type
if (!go.isRequiredParameter(param) && !(param.location === 'client' && go.isClientSideDefault(param.style)) && !param.byValue) {
paramName = `*${paramName}`;
}
return paramName;
}
// converts the Go code model encoding type to the type name in the standard library
export function formatBytesEncoding(enc) {
if (enc === 'URL') {
return 'RawURL';
}
return 'Std';
}
export function formatParamValue(param, imports) {
let paramName = getParamName(param);
switch (param.kind) {
case 'formBodyCollectionParam':
case 'headerCollectionParam':
case 'pathCollectionParam':
case 'queryCollectionParam': {
if (param.collectionFormat === 'multi') {
throw new CodegenError('InternalError', 'multi collection format should have been previously handled');
}
const separator = getDelimiterForCollectionFormat(param.collectionFormat);
const emitConvertOver = function (paramName, format) {
const encodedVar = `encoded${capitalize(paramName)}`;
let content = 'strings.Join(func() []string {\n';
content += `\t\t${encodedVar} := make([]string, len(${paramName}))\n`;
content += `\t\tfor i := 0; i < len(${paramName}); i++ {\n`;
content += `\t\t\t${encodedVar}[i] = ${format}\n\t\t}\n`;
content += `\t\treturn ${encodedVar}\n`;
content += `\t}(), "${separator}")`;
return content;
};
switch (param.type.elementType.kind) {
case 'encodedBytes':
imports.add('encoding/base64');
imports.add('strings');
return emitConvertOver(param.name, `base64.${formatBytesEncoding(param.type.elementType.encoding)}Encoding.EncodeToString(${param.name}[i])`);
case 'string':
imports.add('strings');
return `strings.Join(${paramName}, "${separator}")`;
case 'time':
imports.add('strings');
return emitConvertOver(param.name, `${param.type.elementType.format}(${param.name}[i]).String()`);
default:
imports.add('fmt');
imports.add('strings');
return `strings.Join(strings.Fields(strings.Trim(fmt.Sprint(${paramName}), "[]")), "${separator}")`;
}
}
}
if (param.type.kind === 'time' && param.type.format !== 'timeUnix') {
// for most time types we call methods on time.Time which is why we remove the dereference.
// however, for unix time, we cast to our unixTime helper first so we must keep the dereference.
if (!go.isRequiredParameter(param) && paramName[0] === '*') {
// remove the dereference
paramName = paramName.substring(1);
}
}
return formatValue(paramName, param.type, imports);
}
export function getDelimiterForCollectionFormat(cf) {
switch (cf) {
case 'csv':
return ',';
case 'pipes':
return '|';
case 'ssv':
return ' ';
case 'tsv':
return '\\t';
default:
throw new CodegenError('InternalError', `unhandled CollectionFormat ${cf}`);
}
}
export function formatValue(paramName, type, imports, defef) {
// callers don't have enough context to know if paramName needs to be
// deferenced so we track that here when specified. note that not all
// cases will require paramName to be dereferenced.
let star = '';
if (defef === true) {
star = '*';
}
switch (type.kind) {
case 'constant':
if (type.type === 'string') {
return `string(${star}${paramName})`;
}
imports.add('fmt');
return `fmt.Sprintf("%v", ${star}${paramName})`;
case 'encodedBytes':
// a base-64 encoded value in string format
imports.add('encoding/base64');
return `base64.${formatBytesEncoding(type.encoding)}Encoding.EncodeToString(${paramName})`;
case 'literal':
// cannot use formatLiteralValue() since all values are treated as strings
return `"${type.literal}"`;
case 'scalar':
switch (type.type) {
case 'bool':
imports.add('strconv');
return `strconv.FormatBool(${star}${paramName})`;
case 'float32':
imports.add('strconv');
return `strconv.FormatFloat(float64(${star}${paramName}), 'f', -1, 32)`;
case 'float64':
imports.add('strconv');
return `strconv.FormatFloat(${star}${paramName}, 'f', -1, 64)`;
case 'int32':
imports.add('strconv');
return `strconv.FormatInt(int64(${star}${paramName}), 10)`;
case 'int64':
imports.add('strconv');
return `strconv.FormatInt(${star}${paramName}, 10)`;
default:
throw new CodegenError('InternalError', `unhandled scalar type ${type.type}`);
}
case 'time':
switch (type.format) {
case 'dateTimeRFC1123':
case 'dateTimeRFC3339':
imports.add('time');
return `${paramName}.Format(${type.format === 'dateTimeRFC1123' ? datetimeRFC1123Format : datetimeRFC3339Format})`;
case 'dateType':
return `${paramName}.Format("${dateFormat}")`;
case 'timeRFC3339':
return `timeRFC3339(${star}${paramName}).String()`;
case 'timeUnix':
return `timeUnix(${star}${paramName}).String()`;
}
default:
return `${star}${paramName}`;
}
}
// returns the clientDefaultValue of the specified param.
// this is usually the value in quotes (i.e. a string) however
// it could also be a constant.
export function formatLiteralValue(value, withCast) {
switch (value.type.kind) {
case 'constant':
return value.literal.name;
case 'encodedBytes':
return value.literal;
case 'scalar':
if (!withCast) {
return `${value.literal}`;
}
switch (value.type.type) {
case 'float32':
return `float32(${value.literal})`;
case 'float64':
return `float64(${value.literal})`;
case 'int32':
return `int32(${value.literal})`;
case 'int64':
return `int64(${value.literal})`;
default:
return value.literal;
}
case 'string':
if (value.literal[0] === '"') {
// string is already quoted
return value.literal;
}
return `"${value.literal}"`;
case 'time':
return `"${value.literal}"`;
}
}
// returns true if at least one of the responses has a schema
export function hasSchemaResponse(method) {
switch (method.responseEnvelope.result?.kind) {
case 'anyResult':
case 'modelResult':
case 'monomorphicResult':
case 'polymorphicResult':
return true;
default:
return false;
}
}
// returns the name of the response field within the response envelope
export function getResultFieldName(method) {
const result = method.responseEnvelope.result;
if (!result) {
throw new CodegenError('InternalError', `missing result for method ${method.name}`);
}
switch (result.kind) {
case 'anyResult':
case 'binaryResult':
case 'headAsBooleanResult':
case 'monomorphicResult':
return result.fieldName;
case 'modelResult':
return result.modelType.name;
case 'polymorphicResult':
return result.interface.name;
}
}
export function formatStatusCodes(statusCodes) {
const asHTTPStatus = new Array();
for (const rawCode of statusCodes) {
asHTTPStatus.push(formatStatusCode(rawCode));
}
return asHTTPStatus.join(', ');
}
export function formatStatusCode(statusCode) {
switch (statusCode) {
// 1xx
case 100:
return 'http.StatusContinue';
case 101:
return 'http.StatusSwitchingProtocols';
case 102:
return 'http.StatusProcessing';
case 103:
return 'http.StatusEarlyHints';
// 2xx
case 200:
return 'http.StatusOK';
case 201:
return 'http.StatusCreated';
case 202:
return 'http.StatusAccepted';
case 203:
return 'http.StatusNonAuthoritativeInfo';
case 204:
return 'http.StatusNoContent';
case 205:
return 'http.StatusResetContent';
case 206:
return 'http.StatusPartialContent';
case 207:
return 'http.StatusMultiStatus';
case 208:
return 'http.StatusAlreadyReported';
case 226:
return 'http.StatusIMUsed';
// 3xx
case 300:
return 'http.StatusMultipleChoices';
case 301:
return 'http.StatusMovedPermanently';
case 302:
return 'http.StatusFound';
case 303:
return 'http.StatusSeeOther';
case 304:
return 'http.StatusNotModified';
case 305:
return 'http.StatusUseProxy';
case 307:
return 'http.StatusTemporaryRedirect';
// 4xx
case 400:
return 'http.StatusBadRequest';
case 401:
return 'http.StatusUnauthorized';
case 402:
return 'http.StatusPaymentRequired';
case 403:
return 'http.StatusForbidden';
case 404:
return 'http.StatusNotFound';
case 405:
return 'http.StatusMethodNotAllowed';
case 406:
return 'http.StatusNotAcceptable';
case 407:
return 'http.StatusProxyAuthRequired';
case 408:
return 'http.StatusRequestTimeout';
case 409:
return 'http.StatusConflict';
case 410:
return 'http.StatusGone';
case 411:
return 'http.StatusLengthRequired';
case 412:
return 'http.StatusPreconditionFailed';
case 413:
return 'http.StatusRequestEntityTooLarge';
case 414:
return 'http.StatusRequestURITooLong';
case 415:
return 'http.StatusUnsupportedMediaType';
case 416:
return 'http.StatusRequestedRangeNotSatisfiable';
case 417:
return 'http.StatusExpectationFailed';
case 418:
return 'http.StatusTeapot';
case 421:
return 'http.StatusMisdirectedRequest';
case 422:
return 'http.StatusUnprocessableEntity';
case 423:
return 'http.StatusLocked';
case 424:
return 'http.StatusFailedDependency';
case 425:
return 'http.StatusTooEarly';
case 426:
return 'http.StatusUpgradeRequired';
case 428:
return 'http.StatusPreconditionRequired';
case 429:
return 'http.StatusTooManyRequests';
case 431:
return 'http.StatusRequestHeaderFieldsTooLarge';
case 451:
return 'http.StatusUnavailableForLegalReasons';
// 5xx
case 500:
return 'http.StatusInternalServerError';
case 501:
return 'http.StatusNotImplemented';
case 502:
return 'http.StatusBadGateway';
case 503:
return 'http.StatusServiceUnavailable';
case 504:
return 'http.StatusGatewayTimeout ';
case 505:
return 'http.StatusHTTPVersionNotSupported';
case 506:
return 'http.StatusVariantAlsoNegotiates';
case 507:
return 'http.StatusInsufficientStorage';
case 508:
return 'http.StatusLoopDetected';
case 510:
return 'http.StatusNotExtended';
case 511:
return 'http.StatusNetworkAuthenticationRequired';
default:
throw new CodegenError('InternalError', `unhandled status code ${statusCode}`);
}
}
export function formatCommentAsBulletItem(prefix, docs) {
// first create the comment block. note that it can be multi-line depending on length:
//
// some comment first line
// and it finishes here.
let description = formatDocCommentWithPrefix(prefix, docs);
if (description.length === 0) {
return '';
}
// transform the above to look like this:
//
// - some comment first line
// and it finishes here.
const chunks = description.split('\n');
for (let i = 0; i < chunks.length; ++i) {
if (i === 0) {
chunks[i] = chunks[i].replace('// ', '// - ');
}
else {
chunks[i] = chunks[i].replace('// ', '// ');
}
}
return chunks.join('\n');
}
// conditionally returns a doc comment on an entity that requires a prefix.
// e.g.:
// {Prefix} - {docs.summary}
//
// {docs.description}
export function formatDocCommentWithPrefix(prefix, docs) {
if (!docs.summary && !docs.description) {
return '';
}
let docComment = '';
if (docs.summary) {
docComment = `${comment(`${prefix} - ${docs.summary}`, '//', undefined, commentLength)}\n`;
}
if (docs.description) {
let description = docs.description;
if (docs.summary) {
docComment += '//\n';
}
else {
// only apply the prefix to the description if there was no summary
description = `${prefix} - ${description}`;
}
docComment += `${comment(`${description}`, '//', undefined, commentLength)}\n`;
}
return docComment;
}
// conditionally returns a doc comment
// {docs.summary}
//
// {docs.description}
export function formatDocComment(docs) {
if (!docs.summary && !docs.description) {
return '';
}
let docComment = '';
if (docs.summary) {
docComment = `${comment(docs.summary, '//', undefined, commentLength)}\n`;
}
if (docs.description) {
if (docs.summary) {
docComment += '//\n';
}
docComment += `${comment(docs.description, '//', undefined, commentLength)}\n`;
}
return docComment;
}
export function getParentImport(codeModel) {
const clientPkg = codeModel.packageName;
if (codeModel.options.module) {
return codeModel.options.module.name;
}
else if (codeModel.options.containingModule) {
return codeModel.options.containingModule + '/' + clientPkg;
}
else {
throw new CodegenError('InvalidArgument', 'unable to determine containing module for fakes. specify either the module or containing-module switch');
}
}
export function getBitSizeForNumber(intSize) {
switch (intSize) {
case 'int8':
return '8';
case 'int16':
return '16';
case 'int32':
case 'float32':
return '32';
case 'int64':
case 'float64':
return '64';
}
}
// returns the underlying map/slice element/value type
// if item isn't a map or slice, item is returned
export function recursiveUnwrapMapSlice(item) {
switch (item.kind) {
case 'map':
return recursiveUnwrapMapSlice(item.valueType);
case 'slice':
return recursiveUnwrapMapSlice(item.elementType);
default:
return item;
}
}
// returns a * for optional params
export function star(param) {
return go.isRequiredParameter(param) || param.byValue ? '' : '*';
}
// used by getSerDeFormat to cache results
const serDeFormatCache = new Map();
// returns the wire format for the named model.
// at present this assumes the formats to be mutually exclusive.
export function getSerDeFormat(model, codeModel) {
let serDeFormat = serDeFormatCache.get(model.name);
if (serDeFormat) {
return serDeFormat;
}
// for model-only builds we assume the format to be JSON
if (codeModel.clients.length === 0) {
return 'JSON';
}
// recursively walks the fields in model, updating serDeFormatCache with the model name and specified format
const recursiveWalkModelFields = function (type, serDeFormat) {
type = recursiveUnwrapMapSlice(type);
switch (type.kind) {
case 'interface':
recursiveWalkModelFields(type.rootType, serDeFormat);
for (const possibleType of type.possibleTypes) {
recursiveWalkModelFields(possibleType, serDeFormat);
}
break;
case 'model':
case 'polymorphicModel':
if (serDeFormatCache.has(type.name)) {
// we've already processed this type, don't do it again
return;
}
serDeFormatCache.set(type.name, serDeFormat);
for (const field of type.fields) {
const fieldType = recursiveUnwrapMapSlice(field.type);
recursiveWalkModelFields(fieldType, serDeFormat);
}
break;
}
};
// walk the methods, indexing the model formats
for (const client of codeModel.clients) {
for (const method of client.methods) {
for (const param of method.parameters) {
if (param.kind !== 'bodyParam' || (param.bodyFormat !== 'JSON' && param.bodyFormat !== 'XML')) {
continue;
}
recursiveWalkModelFields(param.type, param.bodyFormat);
}
const resultType = method.responseEnvelope.result;
switch (resultType?.kind) {
case 'anyResult':
if (resultType.format === 'JSON' || resultType.format === 'XML') {
for (const type of Object.values(resultType.httpStatusCodeType)) {
recursiveWalkModelFields(type, resultType.format);
}
}
break;
case 'modelResult':
recursiveWalkModelFields(resultType.modelType, resultType.format);
break;
case 'monomorphicResult':
if (resultType.format === 'JSON' || resultType.format === 'XML') {
recursiveWalkModelFields(resultType.monomorphicType, resultType.format);
}
break;
case 'polymorphicResult':
recursiveWalkModelFields(resultType.interface, resultType.format);
break;
}
}
}
serDeFormat = serDeFormatCache.get(model.name);
if (!serDeFormat) {
// if we get here there are two possibilities
// - we have a bug in the above indexing
// - the model type is unreferenced by any operation
//
// while the former is possible, the latter has the potential to be real.
// regardless of the cause, we will just assume the format to be JSON.
serDeFormat = 'JSON';
}
return serDeFormat;
}
// return combined client parameters for all the clients
export function getAllClientParameters(codeModel) {
const allClientParams = new Array();
for (const clients of codeModel.clients) {
for (const clientParam of values(clients.parameters)) {
if (values(allClientParams).where(param => param.name === clientParam.name).any()) {
continue;
}
allClientParams.push(clientParam);
}
}
allClientParams.sort(sortParametersByRequired);
return allClientParams;
}
// returns common client parameters for all the clients
export function getCommonClientParameters(codeModel) {
const paramCount = new Map();
let numClients = 0; // track client count since we might skip some
for (const clients of codeModel.clients) {
// special cases: some ARM clients always don't contain any parameters (OperationsClient will be depracated in the future)
if (codeModel.type === 'azure-arm' && clients.name.match(/^OperationsClient$/)) {
continue;
}
++numClients;
for (const clientParam of values(clients.parameters)) {
let entry = paramCount.get(clientParam.name);
if (!entry) {
entry = { uses: 0, param: clientParam };
paramCount.set(clientParam.name, entry);
}
++entry.uses;
}
}
// for each param, if its usage count is equal to the
// number of clients, then it's common to all clients
const commonClientParams = new Array();
for (const entry of paramCount.values()) {
if (entry.uses === numClients) {
commonClientParams.push(entry.param);
}
}
return commonClientParams.sort(sortParametersByRequired);
}
/**
* enumerates method parameters and returns them based on kinds
*
* @param method the method containing the parameters to group
* @returns the groups of parameters
*/
export function getMethodParamGroups(method) {
let bodyParam;
const encodedQueryParams = new Array();
const formBodyParams = new Array();
const headerParams = new Array();
const multipartBodyParams = new Array();
const pathParams = new Array();
const partialBodyParams = new Array();
const unencodedQueryParams = new Array();
for (const param of method.parameters) {
switch (param.kind) {
case 'bodyParam':
bodyParam = param;
break;
case 'formBodyCollectionParam':
case 'formBodyScalarParam':
formBodyParams.push(param);
break;
case 'headerCollectionParam':
case 'headerMapParam':
case 'headerScalarParam':
headerParams.push(param);
break;
case 'multipartFormBodyParam':
multipartBodyParams.push(param);
break;
case 'partialBodyParam':
partialBodyParams.push(param);
break;
case 'pathCollectionParam':
case 'pathScalarParam':
pathParams.push(param);
break;
case 'queryCollectionParam':
case 'queryScalarParam':
if (param.isEncoded) {
encodedQueryParams.push(param);
}
else {
unencodedQueryParams.push(param);
}
break;
}
}
return {
bodyParam,
encodedQueryParams,
formBodyParams,
headerParams,
multipartBodyParams,
pathParams,
partialBodyParams,
unencodedQueryParams,
};
}
/** helper for managing indentation levels */
export class indentation {
level;
constructor(level) {
if (level !== undefined) {
this.level = level;
}
else {
// default to one level of indentation
this.level = 1;
}
}
/**
* returns spaces for the current indentation level
*
* @returns a string with the current indentation level
*/
get() {
let indent = '';
for (let i = 0; i < this.level; ++i) {
indent += '\t';
}
return indent;
}
/**
* increments the indentation level
*
* @returns this indentation instance
*/
push() {
++this.level;
return this;
}
/**
* decrements the indentation level
*
* @returns this indentation instance
*/
pop() {
--this.level;
if (this.level < 0) {
throw new CodegenError('InternalError', 'indentation stack underflow');
}
return this;
}
}
/**
* constructs an if block (can expand to include else if as necessary)
*
* @param indent the current indentation helper in scope
* @param ifBlock the if block definition
* @param elseBlock optional else block definition
* @returns the text for the if block
*/
export function buildIfBlock(indent, ifBlock, elseBlock) {
let body = `if ${ifBlock.condition} {\n`;
body += ifBlock.body(indent.push());
body += `${indent.pop().get()}}`;
if (elseBlock) {
body += ' else {\n';
body += elseBlock.body(indent.push());
body += `${indent.pop().get()}}`;
}
return body;
}
/**
* constructs an "if err != nil { return something }" block
*
* @param indent the current indentation helper in scope
* @param errVar the name of the error variable used in the condition
* @param returns the value(s) to return from the control block
* @returns the text for the error check block
*/
export function buildErrCheck(indent, errVar, returns) {
let body = `if ${errVar} != nil {\n`;
body += `${indent.push().get()}return ${returns}\n`;
body += `${indent.pop().get()}}`;
return body;
}
//# sourceMappingURL=helpers.js.map