@azure-tools/typespec-go 0.10.7 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/codegen.go/src/core/clientFactory.d.ts.map +1 -1
  2. package/dist/codegen.go/src/core/clientFactory.js +18 -15
  3. package/dist/codegen.go/src/core/clientFactory.js.map +1 -1
  4. package/dist/codegen.go/src/core/cloudConfig.d.ts.map +1 -1
  5. package/dist/codegen.go/src/core/cloudConfig.js +4 -3
  6. package/dist/codegen.go/src/core/cloudConfig.js.map +1 -1
  7. package/dist/codegen.go/src/core/constants.d.ts.map +1 -1
  8. package/dist/codegen.go/src/core/constants.js +6 -4
  9. package/dist/codegen.go/src/core/constants.js.map +1 -1
  10. package/dist/codegen.go/src/core/example.js +51 -52
  11. package/dist/codegen.go/src/core/example.js.map +1 -1
  12. package/dist/codegen.go/src/core/helpers.d.ts +27 -11
  13. package/dist/codegen.go/src/core/helpers.d.ts.map +1 -1
  14. package/dist/codegen.go/src/core/helpers.js +56 -17
  15. package/dist/codegen.go/src/core/helpers.js.map +1 -1
  16. package/dist/codegen.go/src/core/interfaces.d.ts.map +1 -1
  17. package/dist/codegen.go/src/core/interfaces.js +4 -3
  18. package/dist/codegen.go/src/core/interfaces.js.map +1 -1
  19. package/dist/codegen.go/src/core/models.d.ts.map +1 -1
  20. package/dist/codegen.go/src/core/models.js +289 -230
  21. package/dist/codegen.go/src/core/models.js.map +1 -1
  22. package/dist/codegen.go/src/core/operations.d.ts.map +1 -1
  23. package/dist/codegen.go/src/core/operations.js +689 -407
  24. package/dist/codegen.go/src/core/operations.js.map +1 -1
  25. package/dist/codegen.go/src/core/options.js +3 -2
  26. package/dist/codegen.go/src/core/options.js.map +1 -1
  27. package/dist/codegen.go/src/core/polymorphics.d.ts.map +1 -1
  28. package/dist/codegen.go/src/core/polymorphics.js +55 -48
  29. package/dist/codegen.go/src/core/polymorphics.js.map +1 -1
  30. package/dist/codegen.go/src/core/responses.d.ts.map +1 -1
  31. package/dist/codegen.go/src/core/responses.js +28 -19
  32. package/dist/codegen.go/src/core/responses.js.map +1 -1
  33. package/dist/codegen.go/src/core/version.d.ts.map +1 -1
  34. package/dist/codegen.go/src/core/version.js +3 -2
  35. package/dist/codegen.go/src/core/version.js.map +1 -1
  36. package/dist/codegen.go/src/emitter.js +4 -1
  37. package/dist/codegen.go/src/emitter.js.map +1 -1
  38. package/dist/codegen.go/src/fake/factory.d.ts.map +1 -1
  39. package/dist/codegen.go/src/fake/factory.js +2 -2
  40. package/dist/codegen.go/src/fake/factory.js.map +1 -1
  41. package/dist/codegen.go/src/fake/servers.d.ts.map +1 -1
  42. package/dist/codegen.go/src/fake/servers.js +386 -259
  43. package/dist/codegen.go/src/fake/servers.js.map +1 -1
  44. package/dist/codemodel.go/src/client.d.ts +42 -5
  45. package/dist/codemodel.go/src/client.d.ts.map +1 -1
  46. package/dist/codemodel.go/src/client.js +13 -2
  47. package/dist/codemodel.go/src/client.js.map +1 -1
  48. package/dist/codemodel.go/src/param.d.ts +13 -2
  49. package/dist/codemodel.go/src/param.d.ts.map +1 -1
  50. package/dist/codemodel.go/src/param.js +6 -0
  51. package/dist/codemodel.go/src/param.js.map +1 -1
  52. package/dist/codemodel.go/src/type.d.ts +35 -12
  53. package/dist/codemodel.go/src/type.d.ts.map +1 -1
  54. package/dist/codemodel.go/src/type.js +14 -3
  55. package/dist/codemodel.go/src/type.js.map +1 -1
  56. package/dist/typespec-go/src/tcgcadapter/adapter.d.ts.map +1 -1
  57. package/dist/typespec-go/src/tcgcadapter/adapter.js +1 -2
  58. package/dist/typespec-go/src/tcgcadapter/adapter.js.map +1 -1
  59. package/dist/typespec-go/src/tcgcadapter/clients.d.ts +16 -1
  60. package/dist/typespec-go/src/tcgcadapter/clients.d.ts.map +1 -1
  61. package/dist/typespec-go/src/tcgcadapter/clients.js +205 -89
  62. package/dist/typespec-go/src/tcgcadapter/clients.js.map +1 -1
  63. package/dist/typespec-go/src/tcgcadapter/helpers.d.ts +22 -0
  64. package/dist/typespec-go/src/tcgcadapter/helpers.d.ts.map +1 -1
  65. package/dist/typespec-go/src/tcgcadapter/helpers.js +58 -0
  66. package/dist/typespec-go/src/tcgcadapter/helpers.js.map +1 -1
  67. package/dist/typespec-go/src/tcgcadapter/types.d.ts +23 -1
  68. package/dist/typespec-go/src/tcgcadapter/types.d.ts.map +1 -1
  69. package/dist/typespec-go/src/tcgcadapter/types.js +113 -26
  70. package/dist/typespec-go/src/tcgcadapter/types.js.map +1 -1
  71. package/package.json +11 -10
@@ -43,7 +43,11 @@ export function generateOperations(pkg, target, options) {
43
43
  }
44
44
  imports.add(azureARM ? 'github.com/Azure/azure-sdk-for-go/sdk/azcore/arm' : 'github.com/Azure/azure-sdk-for-go/sdk/azcore');
45
45
  // generate client type
46
- let clientText = helpers.formatDocComment(client.docs);
46
+ let clientText = '';
47
+ if (client.apiVersion) {
48
+ clientText += `const ${client.apiVersion.name} ${go.getTypeDeclaration(client.apiVersion, pkg)} = ${helpers.formatLiteralValue(client.apiVersion.literal, false)}\n\n`;
49
+ }
50
+ clientText += helpers.formatDocComment(client.docs);
47
51
  clientText += "// Don't use this type directly, use ";
48
52
  if (client.instance?.kind === 'constructable' && client.instance.constructors.length === 1) {
49
53
  clientText += `${client.instance.constructors[0].name}() instead.\n`;
@@ -65,8 +69,12 @@ export function generateOperations(pkg, target, options) {
65
69
  else {
66
70
  clientText += 'a constructor function instead.\n';
67
71
  }
72
+ if (client.apiVersion) {
73
+ clientText += `//\n// Generated from API version ${client.apiVersion.literal.literal}\n`;
74
+ }
75
+ const indent = new helpers.Indentation();
68
76
  clientText += `type ${client.name} struct {\n`;
69
- clientText += `\tinternal *${azureARM ? 'arm' : 'azcore'}.Client\n`;
77
+ clientText += `${indent.get()}internal *${azureARM ? 'arm' : 'azcore'}.Client\n`;
70
78
  // check for any optional host params
71
79
  const optionalParams = new Array();
72
80
  const isParamPointer = function (param) {
@@ -82,12 +90,12 @@ export function generateOperations(pkg, target, options) {
82
90
  }
83
91
  if (clientParam.group) {
84
92
  if (!addedGroups.has(clientParam.group.groupName)) {
85
- clientText += `\t${naming.uncapitalize(clientParam.group.groupName)} ${!isParamPointer(clientParam) ? '' : '*'}${clientParam.group.groupName}\n`;
93
+ clientText += `${indent.get()}${naming.uncapitalize(clientParam.group.groupName)} ${!isParamPointer(clientParam) ? '' : '*'}${clientParam.group.groupName}\n`;
86
94
  addedGroups.add(clientParam.group.groupName);
87
95
  }
88
96
  continue;
89
97
  }
90
- clientText += `\t${clientParam.name} `;
98
+ clientText += `${indent.get()}${clientParam.name} `;
91
99
  if (!isParamPointer(clientParam)) {
92
100
  clientText += `${go.getTypeDeclaration(clientParam.type, client.pkg)}\n`;
93
101
  }
@@ -101,7 +109,7 @@ export function generateOperations(pkg, target, options) {
101
109
  }
102
110
  // end of client definition
103
111
  clientText += '}\n\n';
104
- clientText += generateConstructors(client, target, imports);
112
+ clientText += generateConstructors(client, target, imports, indent);
105
113
  // generate client accessors and operations
106
114
  let opText = '';
107
115
  for (const clientAccessor of client.clientAccessors) {
@@ -109,7 +117,7 @@ export function generateOperations(pkg, target, options) {
109
117
  const subClientDecl = go.getTypeDeclaration(clientAccessor.returns, pkg);
110
118
  opText += helpers.formatDocComment(clientAccessor.docs);
111
119
  opText += `func (client *${client.name}) ${clientAccessor.name}(${getAPIParametersSig(clientAccessor, imports)}) *${subClientDecl} {\n`;
112
- opText += `\treturn &${subClientDecl}{\n`;
120
+ opText += `${indent.get()}return &${subClientDecl}{\n`;
113
121
  const initFields = new Array('internal: client.internal');
114
122
  // propagate all client params
115
123
  for (const param of clientAccessor.parameters) {
@@ -129,10 +137,12 @@ export function generateOperations(pkg, target, options) {
129
137
  }
130
138
  }
131
139
  initFields.sort();
140
+ indent.push();
132
141
  for (const initField of initFields) {
133
- opText += `\t\t${initField},\n`;
142
+ opText += `${indent.get()}${initField},\n`;
134
143
  }
135
- opText += '\t}\n}\n\n';
144
+ indent.pop();
145
+ opText += `${indent.get()}}\n}\n\n`;
136
146
  }
137
147
  const nextPageMethods = new Array();
138
148
  for (const method of client.methods) {
@@ -140,21 +150,21 @@ export function generateOperations(pkg, target, options) {
140
150
  // it must be done before the imports are written out
141
151
  if (go.isLROMethod(method)) {
142
152
  // generate Begin method
143
- opText += generateLROBeginMethod(method, imports, options);
153
+ opText += generateLROBeginMethod(method, options, imports, indent);
144
154
  }
145
- opText += generateOperation(method, imports, options);
146
- opText += createProtocolRequest(azureARM, method, imports);
155
+ opText += generateOperation(method, options, imports, indent);
156
+ opText += createProtocolRequest(azureARM, method, imports, indent);
147
157
  if (method.kind !== 'lroMethod') {
148
158
  // LRO responses are handled elsewhere, with the exception of pageable LROs
149
- opText += createProtocolResponse(method, imports);
159
+ opText += createProtocolResponse(method, imports, indent);
150
160
  }
151
- if ((method.kind === 'lroPageableMethod' || method.kind === 'pageableMethod') && method.nextPageMethod && !nextPageMethods.includes(method.nextPageMethod)) {
161
+ if (go.isPageableMethod(method) && method.strategy?.kind === 'nextLink' && method.strategy.method && !nextPageMethods.includes(method.strategy.method)) {
152
162
  // track the next page methods to generate as multiple operations can use the same next page operation
153
- nextPageMethods.push(method.nextPageMethod);
163
+ nextPageMethods.push(method.strategy.method);
154
164
  }
155
165
  }
156
166
  for (const method of nextPageMethods) {
157
- opText += createProtocolRequest(azureARM, method, imports);
167
+ opText += createProtocolRequest(azureARM, method, imports, indent);
158
168
  }
159
169
  // stitch it all together
160
170
  let text = helpers.contentPreamble(pkg);
@@ -173,7 +183,7 @@ export function generateOperations(pkg, target, options) {
173
183
  * @param imports the import manager currently in scope
174
184
  * @returns the client constructor code or the empty string
175
185
  */
176
- function generateConstructors(client, type, imports) {
186
+ function generateConstructors(client, type, imports, indent) {
177
187
  if (client.instance?.kind !== 'constructable') {
178
188
  return '';
179
189
  }
@@ -182,7 +192,7 @@ function generateConstructors(client, type, imports) {
182
192
  if (clientOptions.kind === 'clientOptions') {
183
193
  // for non-ARM, the options type will always be a parameter group
184
194
  ctorText += `// ${clientOptions.name} contains the optional values for creating a [${client.name}].\n`;
185
- ctorText += `type ${clientOptions.name} struct {\n\tazcore.ClientOptions\n`;
195
+ ctorText += `type ${clientOptions.name} struct {\n${indent.get()}azcore.ClientOptions\n`;
186
196
  for (const param of clientOptions.parameters) {
187
197
  if (go.isAPIVersionParameter(param)) {
188
198
  // we use azcore.ClientOptions.APIVersion
@@ -193,9 +203,9 @@ function generateConstructors(client, type, imports) {
193
203
  if (!param.docs.description && !param.docs.summary) {
194
204
  ctorText += '\n';
195
205
  }
196
- ctorText += `\t${helpers.comment(`The default value is ${helpers.formatLiteralValue(param.style.defaultValue, false)}`, '// ')}.\n`;
206
+ ctorText += `${indent.get()}${helpers.comment(`The default value is ${helpers.formatLiteralValue(param.style.defaultValue, false)}`, '// ')}.\n`;
197
207
  }
198
- ctorText += `\t${naming.ensureNameCase(param.name)} *${go.getTypeDeclaration(param.type, client.pkg)}\n`;
208
+ ctorText += `${indent.get()}${naming.ensureNameCase(param.name)} *${go.getTypeDeclaration(param.type, client.pkg)}\n`;
199
209
  }
200
210
  ctorText += '}\n\n';
201
211
  }
@@ -228,7 +238,9 @@ function generateConstructors(client, type, imports) {
228
238
  }
229
239
  const emitProlog = function (optionsTypeName, tokenAuth, plOpts) {
230
240
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
231
- let bodyText = `\tif options == nil {\n\t\toptions = &${optionsTypeName}{}\n\t}\n`;
241
+ let bodyText = `${indent.get()}if options == nil {\n`;
242
+ bodyText += `${indent.push().get()}options = &${optionsTypeName}{}\n`;
243
+ bodyText += `${indent.pop().get()}}\n`;
232
244
  let apiVersionConfig = '';
233
245
  // check if there's an api version parameter
234
246
  let apiVersionParam;
@@ -247,13 +259,15 @@ function generateConstructors(client, type, imports) {
247
259
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud');
248
260
  imports.add('fmt');
249
261
  imports.add('reflect');
250
- bodyText += '\tif reflect.ValueOf(options.Cloud).IsZero() {\n';
251
- bodyText += '\t\toptions.Cloud = cloud.AzurePublic\n\t}\n';
252
- bodyText += '\tc, ok := options.Cloud.Services[ServiceName]\n';
253
- bodyText += '\tif !ok {\n';
254
- bodyText += '\t\treturn nil, fmt.Errorf("provided Cloud field is missing configuration for %s", ServiceName)\n';
255
- bodyText += '\t} else if c.Audience == "" {\n';
256
- bodyText += '\t\treturn nil, fmt.Errorf("provided Cloud field is missing Audience for %s", ServiceName)\n\t}\n';
262
+ bodyText += `${indent.get()}if reflect.ValueOf(options.Cloud).IsZero() {\n`;
263
+ bodyText += `${indent.push().get()}options.Cloud = cloud.AzurePublic\n`;
264
+ bodyText += `${indent.pop().get()}}\n`;
265
+ bodyText += `${indent.get()}c, ok := options.Cloud.Services[ServiceName]\n`;
266
+ bodyText += `${indent.get()}if !ok {\n`;
267
+ bodyText += `${indent.push().get()}return nil, fmt.Errorf("provided Cloud field is missing configuration for %s", ServiceName)\n`;
268
+ bodyText += `${indent.pop().get()}} else if c.Audience == "" {\n`;
269
+ bodyText += `${indent.push().get()}return nil, fmt.Errorf("provided Cloud field is missing Audience for %s", ServiceName)\n`;
270
+ bodyText += `${indent.pop().get()}}\n`;
257
271
  }
258
272
  if (apiVersionParam) {
259
273
  let location;
@@ -273,18 +287,23 @@ function generateConstructors(client, type, imports) {
273
287
  name = apiVersionParam.queryParameter;
274
288
  break;
275
289
  }
290
+ indent.push(); // level 2 for PipelineOptions fields
276
291
  if (name) {
277
- name = `\n\t\t\tName: "${name}",`;
292
+ indent.push(); // level 3 for APIVersionOptions fields
293
+ name = `\n${indent.get()}Name: "${name}",`;
294
+ indent.pop(); // back to level 2
278
295
  }
279
296
  else {
280
297
  name = '';
281
298
  }
282
- apiVersionConfig = `\n\t\tAPIVersion: runtime.APIVersionOptions{${name}\n\t\t\tLocation: runtime.APIVersionLocation${location},\n\t\t},`;
299
+ indent.push(); // level 3 for APIVersionOptions fields
300
+ apiVersionConfig = `\n${indent.pop().get()}APIVersion: runtime.APIVersionOptions{${name}\n${indent.push().get()}Location: runtime.APIVersionLocation${location},\n${indent.pop().get()}},`;
301
+ indent.pop(); // back to level 1
283
302
  if (!plOpts) {
284
303
  apiVersionConfig += '\n';
285
304
  }
286
305
  }
287
- bodyText += `\tcl, err := azcore.NewClient(moduleName, moduleVersion, runtime.PipelineOptions{${apiVersionConfig}${plOpts ?? ''}}, &options.ClientOptions)\n`;
306
+ bodyText += `${indent.get()}cl, err := azcore.NewClient(moduleName, moduleVersion, runtime.PipelineOptions{${apiVersionConfig}${plOpts ?? ''}}, &options.ClientOptions)\n`;
288
307
  return bodyText;
289
308
  };
290
309
  // check if there's a credential parameter
@@ -304,16 +323,19 @@ function generateConstructors(client, type, imports) {
304
323
  switch (clientOptions.kind) {
305
324
  case 'clientOptions': {
306
325
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/policy');
307
- const tokenPolicyOpts = '&policy.BearerTokenOptions{\n\t\t\tInsecureAllowCredentialWithHTTP: options.InsecureAllowCredentialWithHTTP,\n\t\t}';
326
+ indent.push(); // level 2 for PipelineOptions fields
327
+ indent.push(); // level 3 for BearerTokenOptions fields
328
+ const tokenPolicyOpts = `&policy.BearerTokenOptions{\n${indent.get()}InsecureAllowCredentialWithHTTP: options.InsecureAllowCredentialWithHTTP,\n${indent.pop().get()}}`;
308
329
  // we assume a single scope. this is enforced when adapting the data from tcgc
309
- const tokenPolicy = `\n\t\tPerCall: []policy.Policy{\n\t\truntime.NewBearerTokenPolicy(credential, []string{c.Audience + "${helpers.splitScope(credentialParam.type.scopes[0]).scope}"}, ${tokenPolicyOpts}),\n\t\t},\n`;
330
+ const tokenPolicy = `\n${indent.get()}PerCall: []policy.Policy{\n${indent.get()}runtime.NewBearerTokenPolicy(credential, []string{c.Audience + "${helpers.splitScope(credentialParam.type.scopes[0]).scope}"}, ${tokenPolicyOpts}),\n${indent.get()}},\n`;
331
+ indent.pop(); // back to level 1
310
332
  prolog = emitProlog(go.getTypeDeclaration(clientOptions, client.pkg), true, tokenPolicy);
311
333
  break;
312
334
  }
313
335
  case 'armClientOptions':
314
336
  // this is the ARM case
315
337
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/arm');
316
- prolog = '\tcl, err := arm.NewClient(moduleName, moduleVersion, credential, options)\n';
338
+ prolog = `${indent.get()}cl, err := arm.NewClient(moduleName, moduleVersion, credential, options)\n`;
317
339
  break;
318
340
  }
319
341
  break;
@@ -331,9 +353,9 @@ function generateConstructors(client, type, imports) {
331
353
  }
332
354
  ctorText += `func ${constructor.name}(${ctorParams.join(', ')}) (*${client.name}, error) {\n`;
333
355
  ctorText += prolog;
334
- ctorText += '\tif err != nil {\n';
335
- ctorText += '\t\treturn nil, err\n';
336
- ctorText += '\t}\n';
356
+ ctorText += `${indent.get()}if err != nil {\n`;
357
+ ctorText += `${indent.push().get()}return nil, err\n`;
358
+ ctorText += `${indent.pop().get()}}\n`;
337
359
  // handle any client-side defaults
338
360
  if (clientOptions.kind === 'clientOptions') {
339
361
  for (const param of clientOptions.parameters) {
@@ -345,8 +367,10 @@ function generateConstructors(client, type, imports) {
345
367
  else {
346
368
  name = naming.ensureNameCase(param.name);
347
369
  }
348
- ctorText += `\t${param.name} := ${helpers.formatLiteralValue(param.style.defaultValue, false)}\n`;
349
- ctorText += `\tif options.${name} != ${helpers.zeroValue(param)} {\n\t\t${param.name} = ${helpers.star(param.byValue)}options.${name}\n\t}\n`;
370
+ ctorText += `${indent.get()}${param.name} := ${helpers.formatLiteralValue(param.style.defaultValue, false)}\n`;
371
+ ctorText += `${indent.get()}if options.${name} != ${helpers.zeroValue(param)} {\n`;
372
+ ctorText += `${indent.push().get()}${param.name} = ${helpers.star(param.byValue)}options.${name}\n`;
373
+ ctorText += `${indent.pop().get()}}\n`;
350
374
  }
351
375
  }
352
376
  }
@@ -356,15 +380,15 @@ function generateConstructors(client, type, imports) {
356
380
  const endpointParam = client.instance.constructors[0].parameters[0];
357
381
  if (client.instance.endpoint.supplemental.parameters.length > 0) {
358
382
  imports.add('strings');
359
- ctorText += `\thost := "${client.instance.endpoint.supplemental.path}"\n`;
383
+ ctorText += `${indent.get()}host := "${client.instance.endpoint.supplemental.path}"\n`;
360
384
  for (const param of client.instance.endpoint.supplemental.parameters) {
361
- ctorText += `\thost = strings.ReplaceAll(host, "{${param.uriPathSegment}}", ${helpers.formatValue(param.name, param.type, imports)})\n`;
385
+ ctorText += `${indent.get()}host = strings.ReplaceAll(host, "{${param.uriPathSegment}}", ${helpers.formatValue(param.name, param.type, imports)})\n`;
362
386
  }
363
- ctorText += `\t${endpointParam.name} = runtime.JoinPaths(${endpointParam.name}, host)\n`;
387
+ ctorText += `${indent.get()}${endpointParam.name} = runtime.JoinPaths(${endpointParam.name}, host)\n`;
364
388
  }
365
389
  else {
366
390
  // there are no params for the supplemental host, so just append it
367
- ctorText += `\t${endpointParam.name} = runtime.JoinPaths(${endpointParam.name}, "${client.instance.endpoint.supplemental.path}")\n`;
391
+ ctorText += `${indent.get()}${endpointParam.name} = runtime.JoinPaths(${endpointParam.name}, "${client.instance.endpoint.supplemental.path}")\n`;
368
392
  }
369
393
  }
370
394
  // construct client literal
@@ -376,122 +400,147 @@ function generateConstructors(client, type, imports) {
376
400
  break;
377
401
  }
378
402
  }
379
- ctorText += `\t${clientVar} := &${client.name}{\n`;
403
+ ctorText += `${indent.get()}${clientVar} := &${client.name}{\n`;
380
404
  // NOTE: we don't enumerate consolidatedCtorParams here
381
405
  // as any supplemental endpoint params are ephemeral and
382
406
  // consumed during client construction.
407
+ indent.push();
383
408
  for (const parameter of client.parameters) {
384
409
  if (go.isLiteralParameter(parameter.style)) {
385
410
  continue;
386
411
  }
387
412
  // each client field will have a matching parameter with the same name
388
- ctorText += `\t\t${parameter.name}: ${parameter.name},\n`;
413
+ ctorText += `${indent.get()}${parameter.name}: ${parameter.name},\n`;
389
414
  }
390
- ctorText += '\tinternal: cl,\n';
391
- ctorText += '\t}\n';
392
- ctorText += `\treturn ${clientVar}, nil\n`;
415
+ ctorText += `${indent.get()}internal: cl,\n`;
416
+ indent.pop();
417
+ ctorText += `${indent.get()}}\n`;
418
+ ctorText += `${indent.get()}return ${clientVar}, nil\n`;
393
419
  ctorText += '}\n\n';
394
420
  }
395
421
  return ctorText;
396
422
  }
397
423
  // use this to generate the code that will help process values returned in response headers
398
- function formatHeaderResponseValue(method, headerResp, imports, respObj, zeroResp) {
424
+ function formatHeaderResponseValue(method, headerResp, respObj, zeroResp, imports, indent) {
399
425
  // dictionaries are handled slightly different so we do that first
400
426
  if (headerResp.kind === 'headerMapResponse') {
401
427
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/to');
402
428
  imports.add('strings');
403
429
  const headerPrefix = headerResp.headerName;
404
- let text = '\tfor hh := range resp.Header {\n';
405
- text += `\t\tif len(hh) > len("${headerPrefix}") && strings.EqualFold(hh[:len("${headerPrefix}")], "${headerPrefix}") {\n`;
406
- text += `\t\t\tif ${respObj}.${headerResp.fieldName} == nil {\n`;
407
- text += `\t\t\t\t${respObj}.${headerResp.fieldName} = map[string]*string{}\n`;
408
- text += '\t\t\t}\n';
409
- text += `\t\t\t${respObj}.${headerResp.fieldName}[hh[len("${headerPrefix}"):]] = to.Ptr(resp.Header.Get(hh))\n`;
410
- text += '\t\t}\n';
411
- text += '\t}\n';
430
+ let text = `${indent.get()}for hh := range resp.Header {\n`;
431
+ text += `${indent.push().get()}if len(hh) > len("${headerPrefix}") && strings.EqualFold(hh[:len("${headerPrefix}")], "${headerPrefix}") {\n`;
432
+ text += `${indent.push().get()}if ${respObj}.${headerResp.fieldName} == nil {\n`;
433
+ text += `${indent.push().get()}${respObj}.${headerResp.fieldName} = map[string]*string{}\n`;
434
+ text += `${indent.pop().get()}}\n`;
435
+ text += `${indent.get()}${respObj}.${headerResp.fieldName}[hh[len("${headerPrefix}"):]] = to.Ptr(resp.Header.Get(hh))\n`;
436
+ text += `${indent.pop().get()}}\n`;
437
+ text += `${indent.pop().get()}}\n`;
412
438
  return text;
413
439
  }
414
- let text = `\tif val := resp.Header.Get("${headerResp.headerName}"); val != "" {\n`;
440
+ let text = `${indent.get()}if val := resp.Header.Get("${headerResp.headerName}"); val != "" {\n`;
441
+ indent.push();
415
442
  let name = naming.uncapitalize(headerResp.fieldName);
416
443
  let byRef = '&';
417
444
  switch (headerResp.type.kind) {
418
445
  case 'constant':
419
446
  case 'etag':
420
- text += `\t\t${respObj}.${headerResp.fieldName} = (*${go.getTypeDeclaration(headerResp.type, method.receiver.type.pkg)})(&val)\n`;
421
- text += '\t}\n';
447
+ text += `${indent.get()}${respObj}.${headerResp.fieldName} = (*${go.getTypeDeclaration(headerResp.type, method.receiver.type.pkg)})(&val)\n`;
448
+ indent.pop();
449
+ text += `${indent.get()}}\n`;
422
450
  return text;
423
451
  case 'encodedBytes':
424
452
  // a base-64 encoded value in string format
425
453
  imports.add('encoding/base64');
426
- text += `\t\t${name}, err := base64.${helpers.formatBytesEncoding(headerResp.type.encoding)}Encoding.DecodeString(val)\n`;
454
+ text += `${indent.get()}${name}, err := base64.${helpers.formatBytesEncoding(headerResp.type.encoding)}Encoding.DecodeString(val)\n`;
427
455
  byRef = '';
428
456
  break;
429
457
  case 'literal':
430
- text += `\t\t${respObj}.${headerResp.fieldName} = &val\n`;
431
- text += '\t}\n';
458
+ text += `${indent.get()}${respObj}.${headerResp.fieldName} = &val\n`;
459
+ indent.pop();
460
+ text += `${indent.get()}}\n`;
432
461
  return text;
433
462
  case 'scalar':
434
- imports.add('strconv');
435
- switch (headerResp.type.type) {
436
- case 'bool':
437
- text += `\t\t${name}, err := strconv.ParseBool(val)\n`;
438
- break;
439
- case 'float32':
440
- text += `\t\t${name}32, err := strconv.ParseFloat(val, 32)\n`;
441
- text += `\t\t${name} := float32(${name}32)\n`;
442
- break;
443
- case 'float64':
444
- text += `\t\t${name}, err := strconv.ParseFloat(val, 64)\n`;
445
- break;
446
- case 'int32':
447
- text += `\t\t${name}32, err := strconv.ParseInt(val, 10, 32)\n`;
448
- text += `\t\t${name} := int32(${name}32)\n`;
449
- break;
450
- case 'int64':
451
- text += `\t\t${name}, err := strconv.ParseInt(val, 10, 64)\n`;
452
- break;
453
- default:
454
- throw new CodegenError('InternalError', `unhandled scalar type ${headerResp.type.type}`);
455
- }
463
+ text += emitScalarParsing(headerResp.type, 'val', name, imports, indent);
456
464
  break;
457
465
  case 'string':
458
- text += `\t\t${respObj}.${headerResp.fieldName} = &val\n`;
459
- text += '\t}\n';
466
+ text += `${indent.get()}${respObj}.${headerResp.fieldName} = &val\n`;
467
+ text += `${indent.pop().get()}}\n`;
460
468
  return text;
461
469
  case 'time':
462
470
  imports.add('time');
463
471
  switch (headerResp.type.format) {
464
472
  case 'RFC1123':
465
473
  case 'RFC3339':
466
- text += `\t\t${name}, err := time.Parse(${headerResp.type.format === 'RFC1123' ? helpers.RFC1123Format : helpers.RFC3339Format}, val)\n`;
474
+ text += `${indent.get()}${name}, err := time.Parse(${headerResp.type.format === 'RFC1123' ? helpers.RFC1123Format : helpers.RFC3339Format}, val)\n`;
467
475
  break;
468
476
  case 'PlainDate':
469
- text += `\t\t${name}, err := time.Parse(${helpers.plainDateFormat}, val)\n`;
477
+ text += `${indent.get()}${name}, err := time.Parse(${helpers.plainDateFormat}, val)\n`;
470
478
  break;
471
479
  case 'PlainTime':
472
- text += `\t\t${name}, err := time.Parse(${helpers.plainTimeFormat}, val)\n`;
480
+ text += `${indent.get()}${name}, err := time.Parse(${helpers.plainTimeFormat}, val)\n`;
473
481
  break;
474
482
  case 'Unix':
475
483
  imports.add('strconv');
476
484
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/to');
477
- text += '\t\tsec, err := strconv.ParseInt(val, 10, 64)\n';
485
+ text += `${indent.get()}sec, err := strconv.ParseInt(val, 10, 64)\n`;
478
486
  name = 'to.Ptr(time.Unix(sec, 0))';
479
487
  byRef = '';
480
488
  break;
481
489
  }
482
490
  }
483
491
  // NOTE: only cases that required parsing will fall through to here
484
- text += '\t\tif err != nil {\n';
485
- text += `\t\t\treturn ${zeroResp}, err\n`;
486
- text += '\t\t}\n';
487
- text += `\t\t${respObj}.${headerResp.fieldName} = ${byRef}${name}\n`;
488
- text += '\t}\n';
492
+ text += `${indent.get()}if err != nil {\n`;
493
+ text += `${indent.push().get()}return ${zeroResp}, err\n`;
494
+ text += `${indent.pop().get()}}\n`;
495
+ text += `${indent.get()}${respObj}.${headerResp.fieldName} = ${byRef}${name}\n`;
496
+ text += `${indent.pop().get()}}\n`;
489
497
  return text;
490
498
  }
499
+ /**
500
+ * emits the code for parsing scalar types from a string.
501
+ * note that the parsing error result is placed into a
502
+ * local var named "err".
503
+ *
504
+ * @param scalar the type of scalar to parse
505
+ * @param src the source var that contains the scalar in string format
506
+ * @param dst the destination var that contains the result
507
+ * @param imports the import manager currently in scope
508
+ * @param indent the indentation helper currently in scope
509
+ * @returns the scalar parsing code
510
+ */
511
+ function emitScalarParsing(scalar, src, dst, imports, indent) {
512
+ imports.add('strconv');
513
+ switch (scalar.type) {
514
+ case 'bool':
515
+ return `${indent.get()}${dst}, err := strconv.ParseBool(${src})\n`;
516
+ case 'float32':
517
+ return `${indent.get()}${dst}32, err := strconv.ParseFloat(${src}, 32)\n` + `${indent.get()}${dst} := float32(${dst}32)\n`;
518
+ case 'float64':
519
+ return `${indent.get()}${dst}, err := strconv.ParseFloat(${src}, 64)\n`;
520
+ case 'int32':
521
+ return `${indent.get()}${dst}32, err := strconv.ParseInt(${src}, 10, 32)\n` + `${indent.get()}${dst} := int32(${dst}32)\n`;
522
+ case 'int64':
523
+ return `${indent.get()}${dst}, err := strconv.ParseInt(${src}, 10, 64)\n`;
524
+ default:
525
+ throw new CodegenError('InternalError', `unhandled scalar type ${scalar.type}`);
526
+ }
527
+ }
528
+ /**
529
+ * returns the zero value for the specified method.
530
+ * note that the zero value will be different depending
531
+ * on which API for the method is being called.
532
+ *
533
+ * @param method the method to determine the zero value
534
+ * @param apiType the method's API that's being invoked
535
+ * lro - the exported LRO API
536
+ * op - the operation method. for sync methods this is the exported API. for LROs it's the internal method called by Begin*
537
+ * handler - the internal response handler method
538
+ * @returns the zero value
539
+ */
491
540
  function getZeroReturnValue(method, apiType) {
492
541
  let returnType = `${method.returns.name}{}`;
493
542
  if (go.isLROMethod(method)) {
494
- if (apiType === 'api' || apiType === 'op') {
543
+ if (apiType === 'lro' || apiType === 'op') {
495
544
  // the api returns a *Poller[T]
496
545
  // the operation returns an *http.Response
497
546
  returnType = 'nil';
@@ -499,103 +548,223 @@ function getZeroReturnValue(method, apiType) {
499
548
  }
500
549
  return returnType;
501
550
  }
502
- // Helper function to generate nil checks for a dotted path
503
- function generateNilChecks(path, prefix = 'page') {
504
- const segments = path.split('.');
551
+ /**
552
+ * Helper function to generate nil checks for a segmented path
553
+ * e.g. page.Foo != nil && page.Foo.Bar != nil
554
+ *
555
+ * @param segments the field segments
556
+ * @param varName optional variable name containing the fields
557
+ * @param omitLastSegment when true, omits the last item in segments.
558
+ * note that this can cause the function to return the empty string when segments.length === 1
559
+ * @returns the sequence of nil checks
560
+ */
561
+ function generateNilChecks(segments, varName = 'page', omitLastSegment = false) {
505
562
  const checks = [];
506
- for (let i = 0; i < segments.length; i++) {
507
- const currentPath = [prefix, ...segments.slice(0, i + 1)].join('.');
563
+ let segmentCount = segments.length;
564
+ if (omitLastSegment) {
565
+ segmentCount -= 1;
566
+ }
567
+ for (let i = 0; i < segmentCount; i++) {
568
+ const currentPath = [varName, ...segments.map((segment) => segment.name).slice(0, i + 1)].join('.');
508
569
  checks.push(`${currentPath} != nil`);
509
570
  }
510
571
  return checks.join(' && ');
511
572
  }
512
- function emitPagerDefinition(method, imports, options) {
573
+ /**
574
+ * emits code that calls runtime.NewPager
575
+ *
576
+ * @param method the pageable method
577
+ * @param options emitter options
578
+ * @param imports the import manager currently in scope
579
+ * @param indent the indentation helper currently in scope
580
+ * @returns the complete call to runtime.NewPager(...)
581
+ */
582
+ function emitPagerDefinition(method, options, imports, indent) {
513
583
  imports.add('context');
514
584
  let text = `runtime.NewPager(runtime.PagingHandler[${method.returns.name}]{\n`;
515
- text += `\t\tMore: func(page ${method.returns.name}) bool {\n`;
516
- // there is no advancer for single-page pagers
517
- if (method.nextLinkName) {
518
- const nilChecks = generateNilChecks(method.nextLinkName);
519
- text += `\t\t\treturn ${nilChecks} && len(*page.${method.nextLinkName}) > 0\n`;
520
- text += '\t\t},\n';
585
+ text += `${indent.push().get()}More: func(page ${method.returns.name}) bool {\n`;
586
+ indent.push();
587
+ if (method.strategy) {
588
+ const moreForNextLinkPath = function (strategy) {
589
+ const nilChecks = generateNilChecks(strategy.nextLinkPath);
590
+ const nextLinkPath = helpers.buildNextLinkPath(strategy);
591
+ text += `${indent.get()}return ${nilChecks} && len(*page.${nextLinkPath}) > 0\n`;
592
+ };
593
+ switch (method.strategy.kind) {
594
+ case 'continuationToken': {
595
+ switch (method.strategy.responseToken.kind) {
596
+ case 'headerScalarResponse':
597
+ const tokenRespField = method.strategy.responseToken.fieldName;
598
+ text += `${indent.get()}return page.${tokenRespField} != nil && len(*page.${tokenRespField}) > 0\n`;
599
+ break;
600
+ case 'nextLink':
601
+ moreForNextLinkPath(method.strategy.responseToken);
602
+ break;
603
+ default:
604
+ method.strategy.responseToken;
605
+ }
606
+ break;
607
+ }
608
+ case 'nextLink': {
609
+ moreForNextLinkPath(method.strategy);
610
+ break;
611
+ }
612
+ default:
613
+ method.strategy;
614
+ }
521
615
  }
522
616
  else {
523
- text += '\t\t\treturn false\n';
524
- text += '\t\t},\n';
617
+ // there is no advancer for single-page pagers
618
+ text += `${indent.get()}return false\n`;
525
619
  }
526
- text += `\t\tFetcher: func(ctx context.Context, page *${method.returns.name}) (${method.returns.name}, error) {\n`;
620
+ text += `${indent.pop().get()}},\n`; // end More func
621
+ text += `${indent.get()}Fetcher: func(ctx context.Context, page *${method.returns.name}) (${method.returns.name}, error) {\n`;
622
+ indent.push();
527
623
  const reqParams = helpers.getCreateRequestParameters(method);
528
624
  if (options.generateFakes) {
529
- text += `\t\tctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "${method.receiver.type.name}.${fixUpMethodName(method)}")\n`;
530
- }
531
- if (method.nextLinkName) {
532
- let nextLinkVar;
533
- if (method.kind === 'pageableMethod') {
534
- text += '\t\t\tnextLink := ""\n';
535
- nextLinkVar = 'nextLink';
536
- text += '\t\t\tif page != nil {\n';
537
- text += `\t\t\t\tnextLink = *page.${method.nextLinkName}\n\t\t\t}\n`;
538
- }
539
- else {
540
- nextLinkVar = `*page.${method.nextLinkName}`;
541
- }
542
- text += `\t\t\tresp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), ${nextLinkVar}, func(ctx context.Context) (*policy.Request, error) {\n`;
543
- text += `\t\t\t\treturn client.${method.naming.requestMethod}(${reqParams})\n\t\t\t}, `;
544
- // nextPageMethod might be absent in some cases, see https://github.com/Azure/autorest/issues/4393
545
- if (method.nextPageMethod) {
546
- const nextOpParams = helpers.getCreateRequestParametersSig(method.nextPageMethod).split(',');
547
- // keep the parameter names from the name/type tuples and find nextLink param
548
- for (let i = 0; i < nextOpParams.length; ++i) {
549
- const paramName = nextOpParams[i].trim().split(' ')[0];
550
- const paramType = nextOpParams[i].trim().split(' ')[1];
551
- if (paramName.startsWith('next') && paramType === 'string') {
552
- nextOpParams[i] = 'encodedNextLink';
625
+ text += `${indent.get()}ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "${method.receiver.type.name}.${fixUpMethodName(method)}")\n`;
626
+ }
627
+ /** emits code to check the HTTP status code and conditionally call the response handler */
628
+ const emitStatusCodeCheckAndResponse = function (respVarName, indent) {
629
+ let content = `${indent.get()}${helpers.buildIfBlock(indent, {
630
+ condition: `!runtime.HasStatusCode(${respVarName}, http.StatusOK)`,
631
+ body: (indent) => `${indent.get()}return ${getZeroReturnValue(method, 'op')}, runtime.NewResponseError(${respVarName})\n`,
632
+ })}\n`;
633
+ content += `${indent.get()}return client.${method.naming.responseMethod}(${respVarName})\n`;
634
+ return content;
635
+ };
636
+ if (method.strategy) {
637
+ switch (method.strategy.kind) {
638
+ case 'continuationToken': {
639
+ const ms = method.strategy;
640
+ const optionsCopy = 'nextOpts';
641
+ text += `${indent.get()}${optionsCopy} := ${method.optionalParamsGroup.groupName}{}\n`;
642
+ text += `${indent.get()}${helpers.buildIfBlock(indent, {
643
+ condition: `${method.optionalParamsGroup.name} != nil`,
644
+ body: (indent) => `${indent.get()}${optionsCopy} = *${method.optionalParamsGroup.name}\n`,
645
+ })}\n`;
646
+ let respToken;
647
+ let nestedNilChecks = '';
648
+ switch (ms.responseToken.kind) {
649
+ case 'headerScalarResponse':
650
+ respToken = ms.responseToken.fieldName;
651
+ break;
652
+ case 'nextLink':
653
+ respToken = helpers.buildNextLinkPath(ms.responseToken);
654
+ if (ms.responseToken.nextLinkPath.length > 1) {
655
+ // we don't need to check the last field for nil as we'll just
656
+ // assign it to the corresponding field in the options param
657
+ nestedNilChecks = ` && ${generateNilChecks(ms.responseToken.nextLinkPath, 'page', true)}`;
658
+ }
659
+ break;
660
+ }
661
+ text += `${indent.get()}${helpers.buildIfBlock(indent, {
662
+ condition: `page != nil${nestedNilChecks}`,
663
+ body: (indent) => `${indent.get()}${optionsCopy}.${ms.requestToken.name} = page.${respToken}\n`,
664
+ })}\n`;
665
+ text += callCreateRequestWithErrCheck(method, 'req', indent, `&${optionsCopy}`);
666
+ text += callPipelineDoWithErrCheck(method, 'req', 'resp', indent);
667
+ text += emitStatusCodeCheckAndResponse('resp', indent);
668
+ break;
669
+ }
670
+ case 'nextLink': {
671
+ const nextLinkPath = helpers.buildNextLinkPath(method.strategy);
672
+ let nextLinkVar;
673
+ if (method.kind === 'pageableMethod') {
674
+ text += `${indent.get()}nextLink := ""\n`;
675
+ nextLinkVar = 'nextLink';
676
+ text += `${indent.get()}if page != nil {\n`;
677
+ text += `${indent.push().get()}nextLink = *page.${nextLinkPath}\n`;
678
+ text += `${indent.pop().get()}}\n`;
553
679
  }
554
680
  else {
555
- nextOpParams[i] = paramName;
681
+ nextLinkVar = `*page.${nextLinkPath}`;
556
682
  }
683
+ text += `${indent.get()}resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), ${nextLinkVar}, func(ctx context.Context) (*policy.Request, error) {\n`;
684
+ text += `${indent.push().get()}return client.${method.naming.requestMethod}(${reqParams})\n`;
685
+ text += `${indent.pop().get()}}, `;
686
+ // nextPageMethod might be absent in some cases, see https://github.com/Azure/autorest/issues/4393
687
+ if (method.strategy.method) {
688
+ const nextOpParams = helpers.getCreateRequestParametersSig(method.strategy.method).split(',');
689
+ // keep the parameter names from the name/type tuples and find nextLink param
690
+ for (let i = 0; i < nextOpParams.length; ++i) {
691
+ const paramName = nextOpParams[i].trim().split(' ')[0];
692
+ const paramType = nextOpParams[i].trim().split(' ')[1];
693
+ if (paramName.startsWith('next') && paramType === 'string') {
694
+ nextOpParams[i] = 'encodedNextLink';
695
+ }
696
+ else {
697
+ nextOpParams[i] = paramName;
698
+ }
699
+ }
700
+ // add a definition for the nextReq func that uses the nextLinkOperation
701
+ indent.push();
702
+ text += `&runtime.FetcherForNextLinkOptions{\n`;
703
+ text += `${indent.get()}NextReq: func(ctx context.Context, encodedNextLink string) (*policy.Request, error) {\n`;
704
+ text += `${indent.push().get()}return client.${method.strategy.method.name}(${nextOpParams.join(', ')})\n`;
705
+ text += `${indent.pop().get()}},\n`;
706
+ text += `${indent.pop().get()}})\n`;
707
+ }
708
+ else if (method.nextLinkVerb !== 'get') {
709
+ text += `&runtime.FetcherForNextLinkOptions{\n`;
710
+ text += `${indent.push().get()}HTTPVerb: http.Method${naming.capitalize(method.nextLinkVerb)},\n`;
711
+ text += `${indent.pop().get()}})\n`;
712
+ }
713
+ else {
714
+ text += 'nil)\n';
715
+ }
716
+ text += `${indent.get()}if err != nil {\n`;
717
+ text += `${indent.push().get()}return ${method.returns.name}{}, err\n`;
718
+ text += `${indent.pop().get()}}\n`;
719
+ text += `${indent.get()}return client.${method.naming.responseMethod}(resp)\n`;
557
720
  }
558
- // add a definition for the nextReq func that uses the nextLinkOperation
559
- text += '&runtime.FetcherForNextLinkOptions{\n\t\t\t\tNextReq: func(ctx context.Context, encodedNextLink string) (*policy.Request, error) {\n';
560
- text += `\t\t\t\t\treturn client.${method.nextPageMethod.name}(${nextOpParams.join(', ')})\n\t\t\t\t},\n\t\t\t})\n`;
561
- }
562
- else if (method.nextLinkVerb !== 'get') {
563
- text += `&runtime.FetcherForNextLinkOptions{\n\t\t\t\tHTTPVerb: http.Method${naming.capitalize(method.nextLinkVerb)},\n\t\t\t})\n`;
564
- }
565
- else {
566
- text += 'nil)\n';
567
721
  }
568
- text += `\t\t\tif err != nil {\n\t\t\t\treturn ${method.returns.name}{}, err\n\t\t\t}\n`;
569
- text += `\t\t\treturn client.${method.naming.responseMethod}(resp)\n`;
570
- text += '\t\t\t},\n';
571
722
  }
572
723
  else {
573
724
  // this is the singular page case, no fetcher helper required
574
- text += `\t\t\treq, err := client.${method.naming.requestMethod}(${reqParams})\n`;
575
- text += '\t\t\tif err != nil {\n';
576
- text += `\t\t\t\treturn ${method.returns.name}{}, err\n`;
577
- text += '\t\t\t}\n';
578
- text += '\t\t\tresp, err := client.internal.Pipeline().Do(req)\n';
579
- text += '\t\t\tif err != nil {\n';
580
- text += `\t\t\t\treturn ${method.returns.name}{}, err\n`;
581
- text += '\t\t\t}\n';
582
- text += '\t\t\tif !runtime.HasStatusCode(resp, http.StatusOK) {\n';
583
- text += `\t\t\t\treturn ${method.returns.name}{}, runtime.NewResponseError(resp)\n`;
584
- text += '\t\t\t}\n';
585
- text += `\t\t\treturn client.${method.naming.responseMethod}(resp)\n`;
586
- text += '\t\t},\n';
725
+ text += callCreateRequestWithErrCheck(method, 'req', indent);
726
+ text += callPipelineDoWithErrCheck(method, 'req', 'resp', indent);
727
+ text += emitStatusCodeCheckAndResponse('resp', indent);
587
728
  }
729
+ text += `${indent.pop().get()}},\n`; // end Fetcher func
588
730
  if (options.injectSpans) {
589
- text += '\t\tTracer: client.internal.Tracer(),\n';
731
+ text += `${indent.get()}Tracer: client.internal.Tracer(),\n`;
590
732
  }
591
- text += '\t})\n';
733
+ text += `${indent.pop().get()}})\n`; // end runtime.NewPager
592
734
  return text;
593
735
  }
594
- function genApiVersionDoc(apiVersions) {
595
- if (apiVersions.length === 0) {
596
- return '';
597
- }
598
- return `//\n// Generated from API version ${apiVersions.join(', ')}\n`;
736
+ /**
737
+ * emits the call to the *CreateRequest method for an SDK method.
738
+ * if the call returns an error, the error is propagated to the caller.
739
+ *
740
+ * @param method the method to call its *CreateRequest method
741
+ * @param reqVarName the var name of the resultant *policy.Request
742
+ * @param indent the indentation helper currently in scope
743
+ * @param optionsParam optional custom param name for the method options param
744
+ * @returns the call to *CreateRequest with an error check
745
+ */
746
+ function callCreateRequestWithErrCheck(method, reqVarName, indent, optionsParam) {
747
+ const reqParams = helpers.getCreateRequestParameters(method, optionsParam);
748
+ let text = `${indent.get()}${reqVarName}, err := client.${method.naming.requestMethod}(${reqParams})\n`;
749
+ const zeroResp = getZeroReturnValue(method, 'op');
750
+ text += `${indent.get()}${helpers.buildErrCheck(indent, 'err', zeroResp)}\n`;
751
+ return text;
752
+ }
753
+ /**
754
+ * emits the call to the pipeline's Do() method on the internal client.
755
+ * if the call returns an error, the error is propagated to the caller.
756
+ *
757
+ * @param method the method that's making the call
758
+ * @param reqVarName the var name of the *policy.Request
759
+ * @param respVarName the var name of the *http.Response
760
+ * @param indent the indentation helper currently in scope
761
+ * @returns the call to Do() with an error check
762
+ */
763
+ function callPipelineDoWithErrCheck(method, reqVarName, respVarName, indent) {
764
+ let text = `${indent.get()}${respVarName}, err := client.internal.Pipeline().Do(${reqVarName})\n`;
765
+ const zeroResp = getZeroReturnValue(method, 'op');
766
+ text += `${indent.get()}${helpers.buildErrCheck(indent, 'err', zeroResp)}\n`;
767
+ return text;
599
768
  }
600
769
  function genRespErrorDoc(method) {
601
770
  if (!(method.returns.result?.kind === 'headAsBooleanResult') && !go.isPageableMethod(method)) {
@@ -614,7 +783,7 @@ function genRespErrorDoc(method) {
614
783
  function getClientReceiverDefinition(receiver) {
615
784
  return `(${receiver.name} ${receiver.byValue ? '' : '*'}${receiver.type.name})`;
616
785
  }
617
- function generateOperation(method, imports, options) {
786
+ function generateOperation(method, options, imports, indent) {
618
787
  const params = getAPIParametersSig(method, imports);
619
788
  const returns = generateReturnsInfo(method, 'op');
620
789
  let methodName = method.name;
@@ -623,18 +792,16 @@ function generateOperation(method, imports, options) {
623
792
  }
624
793
  let text = '';
625
794
  const respErrDoc = genRespErrorDoc(method);
626
- const apiVerDoc = genApiVersionDoc(method.apiVersions);
627
795
  if (method.docs.summary || method.docs.description) {
628
796
  text += helpers.formatDocCommentWithPrefix(methodName, method.docs);
629
797
  }
630
- else if (respErrDoc.length > 0 || apiVerDoc.length > 0) {
798
+ else if (respErrDoc.length > 0) {
631
799
  // if the method has no doc comment but we're adding other
632
800
  // doc comments, add an empty method name comment. this preserves
633
801
  // existing behavior and makes the docs look better overall.
634
802
  text += `// ${methodName} -\n`;
635
803
  }
636
804
  text += respErrDoc;
637
- text += apiVerDoc;
638
805
  if (go.isLROMethod(method)) {
639
806
  methodName = method.naming.internalMethod;
640
807
  }
@@ -644,63 +811,85 @@ function generateOperation(method, imports, options) {
644
811
  }
645
812
  }
646
813
  text += `func ${getClientReceiverDefinition(method.receiver)} ${methodName}(${params}) (${returns.join(', ')}) {\n`;
647
- const reqParams = helpers.getCreateRequestParameters(method);
648
814
  if (method.kind === 'pageableMethod') {
649
- text += '\treturn ';
650
- text += emitPagerDefinition(method, imports, options);
815
+ text += `${indent.get()}return `;
816
+ text += emitPagerDefinition(method, options, imports, indent);
651
817
  text += '}\n\n';
652
818
  return text;
653
819
  }
654
- text += '\tvar err error\n';
820
+ text += `${indent.get()}var err error\n`;
655
821
  let operationName = `"${method.receiver.type.name}.${fixUpMethodName(method)}"`;
656
822
  if (options.generateFakes && options.injectSpans) {
657
- text += `\tconst operationName = ${operationName}\n`;
823
+ text += `${indent.get()}const operationName = ${operationName}\n`;
658
824
  operationName = 'operationName';
659
825
  }
660
826
  if (options.generateFakes) {
661
- text += `\tctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, ${operationName})\n`;
827
+ text += `${indent.get()}ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, ${operationName})\n`;
662
828
  }
663
829
  if (options.injectSpans) {
664
- text += `\tctx, endSpan := runtime.StartSpan(ctx, ${operationName}, client.internal.Tracer(), nil)\n`;
665
- text += '\tdefer func() { endSpan(err) }()\n';
830
+ text += `${indent.get()}ctx, endSpan := runtime.StartSpan(ctx, ${operationName}, client.internal.Tracer(), nil)\n`;
831
+ text += `${indent.get()}defer func() { endSpan(err) }()\n`;
666
832
  }
667
833
  const zeroResp = getZeroReturnValue(method, 'op');
668
- text += `\treq, err := client.${method.naming.requestMethod}(${reqParams})\n`;
669
- text += '\tif err != nil {\n';
670
- text += `\t\treturn ${zeroResp}, err\n`;
671
- text += '\t}\n';
672
- text += '\thttpResp, err := client.internal.Pipeline().Do(req)\n';
673
- text += '\tif err != nil {\n';
674
- text += `\t\treturn ${zeroResp}, err\n`;
675
- text += '\t}\n';
676
- text += `\tif !runtime.HasStatusCode(httpResp, ${helpers.formatStatusCodes(method.httpStatusCodes)}) {\n`;
677
- text += '\t\terr = runtime.NewResponseError(httpResp)\n';
678
- text += `\t\treturn ${zeroResp}, err\n`;
679
- text += '\t}\n';
834
+ text += callCreateRequestWithErrCheck(method, 'req', indent);
835
+ text += callPipelineDoWithErrCheck(method, 'req', 'httpResp', indent);
836
+ text += `${indent.get()}if !runtime.HasStatusCode(httpResp, ${helpers.formatStatusCodes(method.httpStatusCodes)}) {\n`;
837
+ indent.push();
838
+ text += `${indent.get()}err = runtime.NewResponseError(httpResp)\n`;
839
+ text += `${indent.get()}return ${zeroResp}, err\n`;
840
+ text += `${indent.pop().get()}}\n`;
680
841
  // HAB with headers response is handled in protocol responder
681
842
  if (method.returns.result?.kind === 'headAsBooleanResult' && method.returns.headers.length === 0) {
682
- text += `\treturn ${method.returns.name}{${method.returns.result.fieldName}: httpResp.StatusCode >= 200 && httpResp.StatusCode < 300}, nil\n`;
843
+ text += `${indent.get()}return ${method.returns.name}{${method.returns.result.fieldName}: httpResp.StatusCode >= 200 && httpResp.StatusCode < 300}, nil\n`;
683
844
  }
684
845
  else {
685
846
  if (go.isLROMethod(method)) {
686
- text += '\treturn httpResp, nil\n';
847
+ text += `${indent.get()}return httpResp, nil\n`;
687
848
  }
688
849
  else if (needsResponseHandler(method)) {
689
850
  // also cheating here as at present the only param to the responder is an http.Response
690
- text += `\tresp, err := client.${method.naming.responseMethod}(httpResp)\n`;
691
- text += '\treturn resp, err\n';
851
+ text += `${indent.get()}resp, err := client.${method.naming.responseMethod}(httpResp)\n`;
852
+ text += `${indent.get()}return resp, err\n`;
692
853
  }
693
854
  else if (method.returns.result?.kind === 'binaryResult') {
694
- text += `\treturn ${method.returns.name}{${method.returns.result.fieldName}: httpResp.Body}, nil\n`;
855
+ text += `${indent.get()}return ${method.returns.name}{${method.returns.result.fieldName}: httpResp.Body}, nil\n`;
695
856
  }
696
857
  else {
697
- text += `\treturn ${method.returns.name}{}, nil\n`;
858
+ text += `${indent.get()}return ${method.returns.name}{}, nil\n`;
698
859
  }
699
860
  }
700
861
  text += '}\n\n';
701
862
  return text;
702
863
  }
703
- function createProtocolRequest(azureARM, method, imports) {
864
+ /**
865
+ * emits Go code to set ContentType on a MultipartContent variable if it has a fixed content type.
866
+ * handles both direct MultipartContent and slices of MultipartContent.
867
+ * returns the empty string if there's nothing to set.
868
+ *
869
+ * @param paramName the name of the multipart param
870
+ * @param wireType the underlying type of the multipart param
871
+ * @param indent the indentation helper currently in scope
872
+ * @returns setter code or the empty string
873
+ */
874
+ function emitMultipartContentTypeSetter(paramName, wireType, indent) {
875
+ let text = '';
876
+ const unwrapped = helpers.recursiveUnwrapMapSlice(wireType);
877
+ if (unwrapped.kind !== 'multipartContent' || !unwrapped.contentType) {
878
+ return text;
879
+ }
880
+ switch (wireType.kind) {
881
+ case 'multipartContent':
882
+ text += `${indent.get()}${paramName}.ContentType = ${unwrapped.contentType.literal}\n`;
883
+ break;
884
+ case 'slice':
885
+ text += `${indent.get()}for i := range ${paramName} {\n`;
886
+ text += `${indent.push().get()}${paramName}[i].ContentType = ${unwrapped.contentType.literal}\n`;
887
+ text += `${indent.pop().get()}}\n`;
888
+ break;
889
+ }
890
+ return text;
891
+ }
892
+ function createProtocolRequest(azureARM, method, imports, indent) {
704
893
  let name = method.name;
705
894
  if (method.kind !== 'nextPageMethod') {
706
895
  name = method.naming.requestMethod;
@@ -727,15 +916,15 @@ function createProtocolRequest(azureARM, method, imports) {
727
916
  else if (method.receiver.type.instance?.kind === 'templatedHost') {
728
917
  imports.add('strings');
729
918
  // we have a templated host
730
- text += `\thost := "${method.receiver.type.instance.path}"\n`;
919
+ text += `${indent.get()}host := "${method.receiver.type.instance.path}"\n`;
731
920
  // get all the host params on the client
732
921
  for (const hostParam of hostParams) {
733
- text += `\thost = strings.ReplaceAll(host, "{${hostParam.uriPathSegment}}", ${helpers.formatValue(`client.${hostParam.name}`, hostParam.type, imports)})\n`;
922
+ text += `${indent.get()}host = strings.ReplaceAll(host, "{${hostParam.uriPathSegment}}", ${helpers.formatValue(`client.${hostParam.name}`, hostParam.type, imports)})\n`;
734
923
  }
735
924
  // check for any method local host params
736
925
  for (const param of method.parameters) {
737
926
  if (param.location === 'method' && param.kind === 'uriParam') {
738
- text += `\thost = strings.ReplaceAll(host, "{${param.uriPathSegment}}", ${helpers.formatValue(helpers.getParamName(param), param.type, imports)})\n`;
927
+ text += `${indent.get()}host = strings.ReplaceAll(host, "{${param.uriPathSegment}}", ${helpers.formatValue(helpers.getParamName(param), param.type, imports)})\n`;
739
928
  }
740
929
  }
741
930
  hostParam = 'host';
@@ -758,7 +947,7 @@ function createProtocolRequest(azureARM, method, imports) {
758
947
  const pathContainsParms = pathStr.includes('{');
759
948
  if (hasPathParams || (!pathContainsParms && pathStr.length > 1)) {
760
949
  // there are path params, or the path doesn't contain tokens and is not "/" so emit it
761
- text += `\turlPath := "${method.httpPath}"\n`;
950
+ text += `${indent.get()}urlPath := "${method.httpPath}"\n`;
762
951
  hostParam = `runtime.JoinPaths(${hostParam}, urlPath)`;
763
952
  }
764
953
  // helper to build nil checks for param groups
@@ -775,7 +964,7 @@ function createProtocolRequest(azureARM, method, imports) {
775
964
  if (param.group.required) {
776
965
  optionalParamGroupCheck = '';
777
966
  }
778
- return `\tif ${optionalParamGroupCheck}${client}${paramGroupName}.${naming.capitalize(param.name)} != nil {\n`;
967
+ return `${indent.get()}if ${optionalParamGroupCheck}${client}${paramGroupName}.${naming.capitalize(param.name)} != nil {\n`;
779
968
  };
780
969
  if (hasPathParams) {
781
970
  // swagger defines path params, emit path and replace tokens
@@ -784,7 +973,13 @@ function createProtocolRequest(azureARM, method, imports) {
784
973
  for (const pp of methodParamGroups.pathParams) {
785
974
  let paramValue;
786
975
  let optionalPathSep = false;
787
- if (pp.style !== 'optional') {
976
+ if (pp.style === 'literal') {
977
+ // literals are always scalar types and require no empty checks
978
+ paramValue = helpers.formatParamValue(pp, imports, indent);
979
+ }
980
+ else if (pp.style === 'required' || pp.location === 'client') {
981
+ // NOTE: we include client params here since they behave
982
+ // like required params (i.e. not grouped).
788
983
  // emit check to ensure path param isn't an empty string
789
984
  if (pp.kind === 'pathScalarParam') {
790
985
  const choiceIsString = function (type) {
@@ -794,34 +989,43 @@ function createProtocolRequest(azureARM, method, imports) {
794
989
  if ((pp.type.kind === 'string' || choiceIsString(pp.type)) && !pp.omitEmptyStringCheck) {
795
990
  const paramName = helpers.getParamName(pp);
796
991
  imports.add('errors');
797
- text += `\tif ${paramName} == "" {\n`;
798
- text += `\t\treturn nil, errors.New("parameter ${paramName} cannot be empty")\n`;
799
- text += '\t}\n';
992
+ text += `${indent.get()}if ${paramName} == "" {\n`;
993
+ text += `${indent.push().get()}return nil, errors.New("parameter ${paramName} cannot be empty")\n`;
994
+ text += `${indent.pop().get()}}\n`;
800
995
  }
801
996
  }
802
- paramValue = helpers.formatParamValue(pp, imports);
997
+ paramValue = helpers.formatParamValue(pp, imports, indent);
803
998
  // for collection-based path params, we emit the empty check
804
999
  // after calling helpers.formatParamValue as that will have the
805
1000
  // var name that contains the slice.
806
1001
  if (pp.kind === 'pathCollectionParam') {
807
1002
  const paramName = helpers.getParamName(pp);
808
1003
  const joinedParamName = `${paramName}Param`;
809
- text += `\t${joinedParamName} := ${paramValue}\n`;
1004
+ text += `${indent.get()}${joinedParamName} := ${paramValue}\n`;
810
1005
  imports.add('errors');
811
- text += `\tif len(${joinedParamName}) == 0 {\n`;
812
- text += `\t\treturn nil, errors.New("parameter ${paramName} cannot be empty")\n`;
813
- text += '\t}\n';
1006
+ text += `${indent.get()}if len(${joinedParamName}) == 0 {\n`;
1007
+ text += `${indent.push().get()}return nil, errors.New("parameter ${paramName} cannot be empty")\n`;
1008
+ text += `${indent.pop().get()}}\n`;
814
1009
  paramValue = joinedParamName;
815
1010
  }
816
1011
  }
1012
+ else if (go.isClientSideDefault(pp.style)) {
1013
+ const defaultValue = naming.uncapitalize(pp.name) + 'Default';
1014
+ text += `${indent.get()}${defaultValue} := ${helpers.formatLiteralValue(pp.style.defaultValue, true)}\n`;
1015
+ text += emitParamGroupCheck(pp);
1016
+ text += `${indent.push().get()}${defaultValue} = ${helpers.getParamName(pp)}\n`;
1017
+ text += `${indent.pop().get()}}\n`;
1018
+ paramValue = helpers.formatValue(defaultValue, pp.type, imports);
1019
+ }
817
1020
  else {
818
1021
  // param isn't required, so emit a local var with
819
1022
  // the correct default value, then populate it with
820
1023
  // the optional value when set.
821
1024
  paramValue = `optional${naming.capitalize(pp.name)}`;
822
- text += `\t${paramValue} := ""\n`;
1025
+ text += `${indent.get()}${paramValue} := ""\n`;
823
1026
  text += emitParamGroupCheck(pp);
824
- text += `\t${paramValue} = ${helpers.formatParamValue(pp, imports)}\n\t}\n`;
1027
+ text += `${indent.push().get()}${paramValue} = ${helpers.formatParamValue(pp, imports, indent)}\n`;
1028
+ text += `${indent.pop().get()}}\n`;
825
1029
  // there are two cases for optional path params.
826
1030
  // - /foo/bar/{optional}
827
1031
  // - /foo/bar{/optional}
@@ -838,20 +1042,20 @@ function createProtocolRequest(azureARM, method, imports) {
838
1042
  return paramValue;
839
1043
  };
840
1044
  if (optionalPathSep) {
841
- text += `\tif len(${paramValue}) > 0 {\n`;
842
- text += `\t\t${paramValue} = "/"+${emitPathEscape()}\n`;
843
- text += '\t}\n';
1045
+ text += `${indent.get()}if len(${paramValue}) > 0 {\n`;
1046
+ text += `${indent.push().get()}${paramValue} = "/"+${emitPathEscape()}\n`;
1047
+ text += `${indent.pop().get()}}\n`;
844
1048
  }
845
1049
  else {
846
1050
  paramValue = emitPathEscape();
847
1051
  }
848
- text += `\turlPath = strings.ReplaceAll(urlPath, "{${pp.pathSegment}}", ${paramValue})\n`;
1052
+ text += `${indent.get()}urlPath = strings.ReplaceAll(urlPath, "{${pp.pathSegment}}", ${paramValue})\n`;
849
1053
  }
850
1054
  }
851
- text += `\treq, err := runtime.NewRequest(ctx, http.Method${naming.capitalize(method.httpMethod)}, ${hostParam})\n`;
852
- text += '\tif err != nil {\n';
853
- text += '\t\treturn nil, err\n';
854
- text += '\t}\n';
1055
+ text += `${indent.get()}req, err := runtime.NewRequest(ctx, http.Method${naming.capitalize(method.httpMethod)}, ${hostParam})\n`;
1056
+ text += `${indent.get()}if err != nil {\n`;
1057
+ text += `${indent.push().get()}return nil, err\n`;
1058
+ text += `${indent.pop().get()}}\n`;
855
1059
  // add query parameters
856
1060
  const encodedParams = methodParamGroups.encodedQueryParams;
857
1061
  const unencodedParams = methodParamGroups.unencodedQueryParams;
@@ -859,34 +1063,34 @@ function createProtocolRequest(azureARM, method, imports) {
859
1063
  let qpText = '';
860
1064
  if (qp.location === 'method' && go.isClientSideDefault(qp.style)) {
861
1065
  qpText = emitClientSideDefault(qp, qp.style, (name, val) => {
862
- return `\treqQP.Set(${name}, ${val})`;
863
- }, imports);
1066
+ return `${indent.get()}reqQP.Set(${name}, ${val})`;
1067
+ }, imports, indent);
864
1068
  }
865
1069
  else if (go.isRequiredParameter(qp.style) || go.isLiteralParameter(qp.style) || (qp.location === 'client' && go.isClientSideDefault(qp.style))) {
866
- qpText = `\t${setter}\n`;
1070
+ qpText = `${indent.get()}${setter}\n`;
867
1071
  }
868
1072
  else if (qp.location === 'client' && !qp.group) {
869
1073
  // global optional param
870
- qpText = `\tif client.${qp.name} != nil {\n`;
871
- qpText += `\t\t${setter}\n`;
872
- qpText += '\t}\n';
1074
+ qpText = `${indent.get()}if client.${qp.name} != nil {\n`;
1075
+ qpText += `${indent.push().get()}${setter}\n`;
1076
+ qpText += `${indent.pop().get()}}\n`;
873
1077
  }
874
1078
  else {
875
1079
  qpText = emitParamGroupCheck(qp);
876
- qpText += `\t\t${setter}\n`;
877
- qpText += '\t}\n';
1080
+ qpText += `${indent.push().get()}${setter}\n`;
1081
+ qpText += `${indent.pop().get()}}\n`;
878
1082
  }
879
1083
  return qpText;
880
1084
  };
881
1085
  // emit encoded params first
882
1086
  if (encodedParams.length > 0) {
883
- text += '\treqQP := req.Raw().URL.Query()\n';
1087
+ text += `${indent.get()}reqQP := req.Raw().URL.Query()\n`;
884
1088
  for (const qp of encodedParams.sort((a, b) => {
885
1089
  return helpers.sortAscending(a.queryParameter, b.queryParameter);
886
1090
  })) {
887
1091
  let setter;
888
1092
  if (qp.kind === 'queryCollectionParam' && qp.collectionFormat === 'multi') {
889
- setter = `\tfor _, qv := range ${helpers.getParamName(qp)} {\n`;
1093
+ setter = `for _, qv := range ${helpers.getParamName(qp)} {\n`;
890
1094
  // emit a type conversion for the qv based on the array's element type
891
1095
  let queryVal;
892
1096
  const arrayQP = qp.type;
@@ -908,63 +1112,63 @@ function createProtocolRequest(azureARM, method, imports) {
908
1112
  imports.add('fmt');
909
1113
  queryVal = 'fmt.Sprintf("%v", qv)';
910
1114
  }
911
- setter += `\t\treqQP.Add("${qp.queryParameter}", ${queryVal})\n`;
912
- setter += '\t}';
1115
+ setter += `${indent.push().get()}reqQP.Add("${qp.queryParameter}", ${queryVal})\n`;
1116
+ setter += `${indent.pop().get()}}`;
913
1117
  }
914
1118
  else {
915
1119
  // cannot initialize setter to this value as helpers.formatParamValue() can change imports
916
- setter = `reqQP.Set("${qp.queryParameter}", ${helpers.formatParamValue(qp, imports)})`;
1120
+ setter = `reqQP.Set("${qp.queryParameter}", ${helpers.formatParamValue(qp, imports, indent)})`;
917
1121
  }
918
1122
  text += emitQueryParam(qp, setter);
919
1123
  }
920
- text += '\treq.Raw().URL.RawQuery = reqQP.Encode()\n';
1124
+ text += `${indent.get()}req.Raw().URL.RawQuery = reqQP.Encode()\n`;
921
1125
  }
922
1126
  // tack on any unencoded params to the end
923
1127
  if (unencodedParams.length > 0) {
924
1128
  if (encodedParams.length > 0) {
925
- text += '\tunencodedParams := []string{req.Raw().URL.RawQuery}\n';
1129
+ text += `${indent.get()}unencodedParams := []string{req.Raw().URL.RawQuery}\n`;
926
1130
  }
927
1131
  else {
928
- text += '\tunencodedParams := []string{}\n';
1132
+ text += `${indent.get()}unencodedParams := []string{}\n`;
929
1133
  }
930
1134
  for (const qp of unencodedParams.sort((a, b) => {
931
1135
  return helpers.sortAscending(a.queryParameter, b.queryParameter);
932
1136
  })) {
933
1137
  let setter;
934
1138
  if (qp.kind === 'queryCollectionParam' && qp.collectionFormat === 'multi') {
935
- setter = `\tfor _, qv := range ${helpers.getParamName(qp)} {\n`;
936
- setter += `\t\tunencodedParams = append(unencodedParams, "${qp.queryParameter}="+qv)\n`;
937
- setter += '\t}';
1139
+ setter = `for _, qv := range ${helpers.getParamName(qp)} {\n`;
1140
+ setter += `${indent.push().get()}unencodedParams = append(unencodedParams, "${qp.queryParameter}="+qv)\n`;
1141
+ setter += `${indent.pop().get()}}`;
938
1142
  }
939
1143
  else {
940
- setter = `unencodedParams = append(unencodedParams, "${qp.queryParameter}="+${helpers.formatParamValue(qp, imports)})`;
1144
+ setter = `unencodedParams = append(unencodedParams, "${qp.queryParameter}="+${helpers.formatParamValue(qp, imports, indent)})`;
941
1145
  }
942
1146
  text += emitQueryParam(qp, setter);
943
1147
  }
944
1148
  imports.add('strings');
945
- text += '\treq.Raw().URL.RawQuery = strings.Join(unencodedParams, "&")\n';
1149
+ text += `${indent.get()}req.Raw().URL.RawQuery = strings.Join(unencodedParams, "&")\n`;
946
1150
  }
947
1151
  if (method.kind !== 'nextPageMethod' && method.returns.result?.kind === 'binaryResult') {
948
1152
  // skip auto-body downloading for binary stream responses
949
- text += '\truntime.SkipBodyDownload(req)\n';
1153
+ text += `${indent.get()}runtime.SkipBodyDownload(req)\n`;
950
1154
  }
951
1155
  // add specific request headers
952
- const emitHeaderSet = function (headerParam, prefix) {
1156
+ const emitHeaderSet = function (headerParam) {
953
1157
  if (headerParam.kind === 'headerMapParam') {
954
- let headerText = `${prefix}for k, v := range ${helpers.getParamName(headerParam)} {\n`;
955
- headerText += `${prefix}\tif v != nil {\n`;
956
- headerText += `${prefix}\t\treq.Raw().Header["${headerParam.headerName}"+k] = []string{*v}\n`;
957
- headerText += `${prefix}}\n`;
958
- headerText += `${prefix}}\n`;
1158
+ let headerText = `${indent.get()}for k, v := range ${helpers.getParamName(headerParam)} {\n`;
1159
+ headerText += `${indent.push().get()}if v != nil {\n`;
1160
+ headerText += `${indent.push().get()}req.Raw().Header["${headerParam.headerName}"+k] = []string{*v}\n`;
1161
+ headerText += `${indent.pop().get()}}\n`;
1162
+ headerText += `${indent.pop().get()}}\n`;
959
1163
  return headerText;
960
1164
  }
961
1165
  else if (headerParam.location === 'method' && go.isClientSideDefault(headerParam.style)) {
962
1166
  return emitClientSideDefault(headerParam, headerParam.style, (name, val) => {
963
- return `${prefix}req.Raw().Header[${name}] = []string{${val}}`;
964
- }, imports);
1167
+ return `${indent.get()}req.Raw().Header[${name}] = []string{${val}}`;
1168
+ }, imports, indent);
965
1169
  }
966
1170
  else {
967
- return `${prefix}req.Raw().Header["${headerParam.headerName}"] = []string{${helpers.formatParamValue(headerParam, imports)}}\n`;
1171
+ return `${indent.get()}req.Raw().Header["${headerParam.headerName}"] = []string{${helpers.formatParamValue(headerParam, imports, indent)}}\n`;
968
1172
  }
969
1173
  };
970
1174
  let contentType;
@@ -980,21 +1184,25 @@ function createProtocolRequest(azureARM, method, imports) {
980
1184
  // to handle cases where the body param is optional. we don't want to set
981
1185
  // the content-type if the body is nil.
982
1186
  // we do it like this as tsp specifies content-type while swagger does not.
983
- contentType = helpers.formatParamValue(param, imports);
1187
+ contentType = helpers.formatParamValue(param, imports, indent);
984
1188
  }
985
1189
  else if (go.isRequiredParameter(param.style) || go.isLiteralParameter(param.style) || go.isClientSideDefault(param.style)) {
986
- text += emitHeaderSet(param, '\t');
1190
+ text += emitHeaderSet(param);
987
1191
  }
988
1192
  else if (param.location === 'client' && !param.group) {
989
1193
  // global optional param
990
- text += `\tif client.${param.name} != nil {\n`;
991
- text += emitHeaderSet(param, '\t');
992
- text += '\t}\n';
1194
+ text += `${indent.get()}if client.${param.name} != nil {\n`;
1195
+ indent.push();
1196
+ text += emitHeaderSet(param);
1197
+ indent.pop();
1198
+ text += `${indent.get()}}\n`;
993
1199
  }
994
1200
  else {
995
1201
  text += emitParamGroupCheck(param);
996
- text += emitHeaderSet(param, '\t\t');
997
- text += '\t}\n';
1202
+ indent.push();
1203
+ text += emitHeaderSet(param);
1204
+ indent.pop();
1205
+ text += `${indent.get()}}\n`;
998
1206
  }
999
1207
  }
1000
1208
  // note that these are mutually exclusive
@@ -1003,13 +1211,36 @@ function createProtocolRequest(azureARM, method, imports) {
1003
1211
  const multipartBodyParams = methodParamGroups.multipartBodyParams;
1004
1212
  const partialBodyParams = methodParamGroups.partialBodyParams;
1005
1213
  const emitSetBodyWithErrCheck = function (setBodyParam, contentType) {
1006
- let content = `if err := ${setBodyParam}; err != nil {\n\treturn nil, err\n}\n;`;
1214
+ let content = '';
1007
1215
  if (contentType) {
1008
- content = `req.Raw().Header["Content-Type"] = []string{${contentType}}\n` + content;
1216
+ content += `${indent.get()}req.Raw().Header["Content-Type"] = []string{${contentType}}\n`;
1009
1217
  }
1218
+ content += `${indent.get()}if err := ${setBodyParam}; err != nil {\n`;
1219
+ content += `${indent.push().get()}return nil, err\n`;
1220
+ content += `${indent.pop().get()}}\n`;
1010
1221
  return content;
1011
1222
  };
1012
1223
  if (bodyParam) {
1224
+ /** returns the source value for the content type */
1225
+ const getContentTypeValue = function (src) {
1226
+ switch (src.kind) {
1227
+ case 'literal':
1228
+ return helpers.formatLiteralValue(src, false);
1229
+ case 'parameterRef': {
1230
+ // find the param
1231
+ for (const param of method.parameters) {
1232
+ if (param.kind === 'headerScalarParam' && param.name === src.name) {
1233
+ let paramName = helpers.getParamName(param);
1234
+ if (param.type.kind === 'constant') {
1235
+ paramName = `string(${paramName})`;
1236
+ }
1237
+ return paramName;
1238
+ }
1239
+ }
1240
+ throw new CodegenError('InternalError', `didn't find parameter reference ${src.name}`);
1241
+ }
1242
+ }
1243
+ };
1013
1244
  if (bodyParam.bodyFormat === 'JSON' || bodyParam.bodyFormat === 'XML') {
1014
1245
  // default to the body param name
1015
1246
  let body = helpers.getParamName(bodyParam);
@@ -1020,7 +1251,8 @@ function createProtocolRequest(azureARM, method, imports) {
1020
1251
  else if (bodyParam.bodyFormat === 'XML' && bodyParam.type.kind === 'slice') {
1021
1252
  // for XML payloads, create a wrapper type if the payload is an array
1022
1253
  imports.add('encoding/xml');
1023
- text += '\ttype wrapper struct {\n';
1254
+ text += `${indent.get()}type wrapper struct {\n`;
1255
+ indent.push();
1024
1256
  let tagName;
1025
1257
  if (bodyParam.xml?.wrapper) {
1026
1258
  tagName = bodyParam.xml.wrapper;
@@ -1028,14 +1260,14 @@ function createProtocolRequest(azureARM, method, imports) {
1028
1260
  else {
1029
1261
  tagName = go.getTypeDeclaration(bodyParam.type, method.receiver.type.pkg);
1030
1262
  }
1031
- text += `\t\tXMLName xml.Name \`xml:"${tagName}"\`\n`;
1263
+ text += `${indent.get()}XMLName xml.Name \`xml:"${tagName}"\`\n`;
1032
1264
  const fieldName = naming.capitalize(bodyParam.name);
1033
1265
  let tag = go.getTypeDeclaration(bodyParam.type.elementType, method.receiver.type.pkg);
1034
1266
  if (bodyParam.type.elementType.kind === 'model' && bodyParam.type.elementType.xml?.name) {
1035
1267
  tag = bodyParam.type.elementType.xml.name;
1036
1268
  }
1037
- text += `\t\t${fieldName} *${go.getTypeDeclaration(bodyParam.type, method.receiver.type.pkg)} \`xml:"${tag}"\`\n`;
1038
- text += '\t}\n';
1269
+ text += `${indent.get()}${fieldName} *${go.getTypeDeclaration(bodyParam.type, method.receiver.type.pkg)} \`xml:"${tag}"\`\n`;
1270
+ text += `${indent.pop().get()}}\n`;
1039
1271
  let addr = '&';
1040
1272
  if (!go.isRequiredParameter(bodyParam.style) && !bodyParam.byValue) {
1041
1273
  addr = '';
@@ -1055,19 +1287,19 @@ function createProtocolRequest(azureARM, method, imports) {
1055
1287
  elementPtr = '';
1056
1288
  }
1057
1289
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime');
1058
- text += `\taux := make([]${elementPtr}datetime.${timeInfo?.format}, len(${body}))\n`;
1059
- text += `\tfor i := 0; i < len(${body}); i++ {\n`;
1060
- text += `\t\taux[i] = (${elementPtr}datetime.${timeInfo?.format})(${body}[i])\n`;
1061
- text += '\t}\n';
1290
+ text += `${indent.get()}aux := make([]${elementPtr}datetime.${timeInfo?.format}, len(${body}))\n`;
1291
+ text += `${indent.get()}for i := 0; i < len(${body}); i++ {\n`;
1292
+ text += `${indent.push().get()}aux[i] = (${elementPtr}datetime.${timeInfo?.format})(${body}[i])\n`;
1293
+ text += `${indent.pop().get()}}\n`;
1062
1294
  body = 'aux';
1063
1295
  }
1064
1296
  else if (isMapOfDateTime(bodyParam.type)) {
1065
1297
  let timeType = isMapOfDateTime(bodyParam.type);
1066
1298
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime');
1067
- text += `\taux := map[string]*datetime.${timeType}{}\n`;
1068
- text += `\tfor k, v := range ${body} {\n`;
1069
- text += `\t\taux[k] = (*datetime.${timeType})(v)\n`;
1070
- text += '\t}\n';
1299
+ text += `${indent.get()}aux := map[string]*datetime.${timeType}{}\n`;
1300
+ text += `${indent.get()}for k, v := range ${body} {\n`;
1301
+ text += `${indent.push().get()}aux[k] = (*datetime.${timeType})(v)\n`;
1302
+ text += `${indent.pop().get()}}\n`;
1071
1303
  body = 'aux';
1072
1304
  }
1073
1305
  let setBody = `runtime.MarshalAs${getMediaFormat(bodyParam.type, bodyParam.bodyFormat, `req, ${body}`)}`;
@@ -1077,134 +1309,164 @@ function createProtocolRequest(azureARM, method, imports) {
1077
1309
  setBody = `req.SetBody(streaming.NopCloser(bytes.NewReader(${body})), "application/${bodyParam.bodyFormat.toLowerCase()}")`;
1078
1310
  }
1079
1311
  if (go.isRequiredParameter(bodyParam.style) || go.isLiteralParameter(bodyParam.style)) {
1080
- text += `\t${emitSetBodyWithErrCheck(setBody, contentType)}`;
1081
- text += '\treturn req, nil\n';
1312
+ text += emitSetBodyWithErrCheck(setBody, contentType);
1313
+ text += `${indent.get()}return req, nil\n`;
1082
1314
  }
1083
1315
  else {
1084
1316
  text += emitParamGroupCheck(bodyParam);
1085
- text += `\t${emitSetBodyWithErrCheck(setBody, contentType)}`;
1086
- text += '\t\treturn req, nil\n';
1087
- text += '\t}\n';
1088
- text += '\treturn req, nil\n';
1317
+ indent.push();
1318
+ text += emitSetBodyWithErrCheck(setBody, contentType);
1319
+ text += `${indent.get()}return req, nil\n`;
1320
+ indent.pop();
1321
+ text += `${indent.get()}}\n`;
1322
+ text += `${indent.get()}return req, nil\n`;
1089
1323
  }
1090
1324
  }
1091
1325
  else if (bodyParam.bodyFormat === 'binary') {
1092
1326
  if (go.isRequiredParameter(bodyParam.style)) {
1093
- text += `\t${emitSetBodyWithErrCheck(`req.SetBody(${bodyParam.name}, ${bodyParam.contentType})`, contentType)}`;
1094
- text += '\treturn req, nil\n';
1327
+ text += emitSetBodyWithErrCheck(`req.SetBody(${bodyParam.name}, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1328
+ text += `${indent.get()}return req, nil\n`;
1095
1329
  }
1096
1330
  else {
1097
1331
  text += emitParamGroupCheck(bodyParam);
1098
- text += `\t${emitSetBodyWithErrCheck(`req.SetBody(${helpers.getParamName(bodyParam)}, ${bodyParam.contentType})`, contentType)}`;
1099
- text += '\treturn req, nil\n';
1100
- text += '\t}\n';
1101
- text += '\treturn req, nil\n';
1332
+ indent.push();
1333
+ text += emitSetBodyWithErrCheck(`req.SetBody(${helpers.getParamName(bodyParam)}, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1334
+ text += `${indent.get()}return req, nil\n`;
1335
+ indent.pop();
1336
+ text += `${indent.get()}}\n`;
1337
+ text += `${indent.get()}return req, nil\n`;
1102
1338
  }
1103
1339
  }
1104
1340
  else if (bodyParam.bodyFormat === 'Text') {
1105
1341
  imports.add('strings');
1106
1342
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
1107
1343
  if (go.isRequiredParameter(bodyParam.style)) {
1108
- text += `\tbody := streaming.NopCloser(strings.NewReader(${bodyParam.name}))\n`;
1109
- text += `\t${emitSetBodyWithErrCheck(`req.SetBody(body, ${bodyParam.contentType})`, contentType)}`;
1110
- text += '\treturn req, nil\n';
1344
+ text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${bodyParam.name}))\n`;
1345
+ text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1346
+ text += `${indent.get()}return req, nil\n`;
1111
1347
  }
1112
1348
  else {
1113
1349
  text += emitParamGroupCheck(bodyParam);
1114
- text += `\tbody := streaming.NopCloser(strings.NewReader(${helpers.getParamName(bodyParam)}))\n`;
1115
- text += `\t${emitSetBodyWithErrCheck(`req.SetBody(body, ${bodyParam.contentType})`, contentType)}`;
1116
- text += '\treturn req, nil\n';
1117
- text += '\t}\n';
1118
- text += '\treturn req, nil\n';
1350
+ indent.push();
1351
+ text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${helpers.getParamName(bodyParam)}))\n`;
1352
+ text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1353
+ text += `${indent.get()}return req, nil\n`;
1354
+ indent.pop();
1355
+ text += `${indent.get()}}\n`;
1356
+ text += `${indent.get()}return req, nil\n`;
1119
1357
  }
1120
1358
  }
1121
1359
  }
1122
1360
  else if (partialBodyParams.length > 0) {
1123
1361
  // partial body params are discrete params that are all fields within an internal struct.
1124
1362
  // define and instantiate an instance of the wire type, using the values from each param.
1125
- text += '\tbody := struct {\n';
1363
+ text += `${indent.get()}body := struct {\n`;
1364
+ indent.push();
1126
1365
  for (const partialBodyParam of partialBodyParams) {
1127
- text += `\t\t${naming.capitalize(partialBodyParam.serializedName)} ${helpers.star(partialBodyParam.byValue)}${go.getTypeDeclaration(partialBodyParam.type, method.receiver.type.pkg)} \`${partialBodyParam.format.toLowerCase()}:"${partialBodyParam.serializedName}"\`\n`;
1366
+ text += `${indent.get()}${naming.capitalize(partialBodyParam.serializedName)} ${helpers.star(partialBodyParam.byValue)}${go.getTypeDeclaration(partialBodyParam.type, method.receiver.type.pkg)} \`${partialBodyParam.format.toLowerCase()}:"${partialBodyParam.serializedName}"\`\n`;
1128
1367
  }
1129
- text += '\t}{\n';
1368
+ indent.pop();
1369
+ text += `${indent.get()}}{\n`;
1370
+ indent.push();
1130
1371
  // required params are emitted as initializers in the struct literal
1131
1372
  for (const partialBodyParam of partialBodyParams) {
1132
1373
  if (go.isRequiredParameter(partialBodyParam.style)) {
1133
- text += `\t\t${naming.capitalize(partialBodyParam.serializedName)}: ${naming.uncapitalize(partialBodyParam.name)},\n`;
1374
+ text += `${indent.get()}${naming.capitalize(partialBodyParam.serializedName)}: ${naming.uncapitalize(partialBodyParam.name)},\n`;
1134
1375
  }
1135
1376
  }
1136
- text += '\t}\n';
1377
+ indent.pop();
1378
+ text += `${indent.get()}}\n`;
1137
1379
  // now populate any optional params from the options type
1138
1380
  for (const partialBodyParam of partialBodyParams) {
1139
1381
  if (!go.isRequiredParameter(partialBodyParam.style)) {
1140
1382
  text += emitParamGroupCheck(partialBodyParam);
1141
- text += `\t\tbody.${naming.capitalize(partialBodyParam.serializedName)} = options.${naming.capitalize(partialBodyParam.name)}\n\t}\n`;
1383
+ text += `${indent.push().get()}body.${naming.capitalize(partialBodyParam.serializedName)} = options.${naming.capitalize(partialBodyParam.name)}\n`;
1384
+ text += `${indent.pop().get()}}\n`;
1142
1385
  }
1143
1386
  }
1144
1387
  // TODO: spread params are JSON only https://github.com/Azure/autorest.go/issues/1455
1145
- text += '\treq.Raw().Header["Content-Type"] = []string{"application/json"}\n';
1146
- text += '\tif err := runtime.MarshalAsJSON(req, body); err != nil {\n\t\treturn nil, err\n\t}\n';
1147
- text += '\treturn req, nil\n';
1388
+ text += `${indent.get()}req.Raw().Header["Content-Type"] = []string{"application/json"}\n`;
1389
+ text += `${indent.get()}if err := runtime.MarshalAsJSON(req, body); err != nil {\n`;
1390
+ text += `${indent.push().get()}return nil, err\n`;
1391
+ text += `${indent.pop().get()}}\n`;
1392
+ text += `${indent.get()}return req, nil\n`;
1148
1393
  }
1149
1394
  else if (multipartBodyParams.length > 0) {
1395
+ // emit content type setters for direct MultipartContent params with a fixed content type.
1396
+ // this handles the case where the param itself is a MultipartContent or a slice of them
1397
+ // (i.e. not wrapped in a model struct with toMultipartFormData()).
1398
+ // note that tsp only allows homogeneous content types, which is why it's safe
1399
+ // to unwrap the first param's type and check its contentType field.
1400
+ text += emitMultipartContentTypeSetter(multipartBodyParams[0].name, multipartBodyParams[0].type, indent);
1150
1401
  if (multipartBodyParams.length === 1 && multipartBodyParams[0].type.kind === 'model' && multipartBodyParams[0].type.annotations.multipartFormData) {
1151
- text += `\tformData, err := ${multipartBodyParams[0].name}.toMultipartFormData()\n`;
1152
- text += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
1402
+ // emit content type setters for model fields that have a fixed content type.
1403
+ // toMultipartFormData() converts the model to map[string]any but doesn't set ContentType,
1404
+ // so we must set it on each MultipartContent field before the conversion.
1405
+ for (const field of multipartBodyParams[0].type.fields) {
1406
+ text += emitMultipartContentTypeSetter(`${multipartBodyParams[0].name}.${field.name}`, field.type, indent);
1407
+ }
1408
+ text += `${indent.get()}formData, err := ${multipartBodyParams[0].name}.toMultipartFormData()\n`;
1409
+ text += `${indent.get()}if err != nil {\n`;
1410
+ text += `${indent.push().get()}return nil, err\n`;
1411
+ text += `${indent.pop().get()}}\n`;
1153
1412
  }
1154
1413
  else {
1155
- text += '\tformData := map[string]any{}\n';
1414
+ text += `${indent.get()}formData := map[string]any{}\n`;
1156
1415
  for (const param of multipartBodyParams) {
1157
1416
  const setter = `formData["${param.name}"] = ${helpers.getParamName(param)}`;
1158
1417
  if (go.isRequiredParameter(param.style)) {
1159
- text += `\t${setter}\n`;
1418
+ text += `${indent.get()}${setter}\n`;
1160
1419
  }
1161
1420
  else {
1162
1421
  text += emitParamGroupCheck(param);
1163
- text += `\t${setter}\n\t}\n`;
1422
+ text += `${indent.push().get()}${setter}\n`;
1423
+ text += `${indent.pop().get()}}\n`;
1164
1424
  }
1165
1425
  }
1166
1426
  }
1167
- text += '\tif err := runtime.SetMultipartFormData(req, formData); err != nil {\n\t\treturn nil, err\n\t}\n';
1168
- text += '\treturn req, nil\n';
1427
+ text += `${indent.get()}if err := runtime.SetMultipartFormData(req, formData); err != nil {\n`;
1428
+ text += `${indent.push().get()}return nil, err\n`;
1429
+ text += `${indent.pop().get()}}\n`;
1430
+ text += `${indent.get()}return req, nil\n`;
1169
1431
  }
1170
1432
  else if (formBodyParams.length > 0) {
1171
1433
  const emitFormData = function (param, setter) {
1172
1434
  let formDataText = '';
1173
1435
  if (go.isRequiredParameter(param.style)) {
1174
- formDataText = `\t${setter}\n`;
1436
+ formDataText = `${indent.get()}${setter}\n`;
1175
1437
  }
1176
1438
  else {
1177
1439
  formDataText = emitParamGroupCheck(param);
1178
- formDataText += `\t\t${setter}\n`;
1179
- formDataText += '\t}\n';
1440
+ formDataText += `${indent.push().get()}${setter}\n`;
1441
+ formDataText += `${indent.pop().get()}}\n`;
1180
1442
  }
1181
1443
  return formDataText;
1182
1444
  };
1183
1445
  imports.add('net/url');
1184
1446
  imports.add('strings');
1185
1447
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
1186
- text += '\tformData := url.Values{}\n';
1448
+ text += `${indent.get()}formData := url.Values{}\n`;
1187
1449
  // find all the form body params
1188
1450
  for (const param of formBodyParams) {
1189
- const setter = `formData.Set("${param.formDataName}", ${helpers.formatParamValue(param, imports)})`;
1451
+ const setter = `formData.Set("${param.formDataName}", ${helpers.formatParamValue(param, imports, indent)})`;
1190
1452
  text += emitFormData(param, setter);
1191
1453
  }
1192
- text += '\tbody := streaming.NopCloser(strings.NewReader(formData.Encode()))\n';
1193
- text += `\t${emitSetBodyWithErrCheck('req.SetBody(body, "application/x-www-form-urlencoded")')}`;
1194
- text += '\treturn req, nil\n';
1454
+ text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(formData.Encode()))\n`;
1455
+ text += emitSetBodyWithErrCheck('req.SetBody(body, "application/x-www-form-urlencoded")');
1456
+ text += `${indent.get()}return req, nil\n`;
1195
1457
  }
1196
1458
  else {
1197
- text += '\treturn req, nil\n';
1459
+ text += `${indent.get()}return req, nil\n`;
1198
1460
  }
1199
1461
  text += '}\n\n';
1200
1462
  return text;
1201
1463
  }
1202
- function emitClientSideDefault(param, csd, setterFormat, imports) {
1464
+ function emitClientSideDefault(param, csd, setterFormat, imports, indent) {
1203
1465
  const defaultVar = naming.uncapitalize(param.name) + 'Default';
1204
- let text = `\t${defaultVar} := ${helpers.formatLiteralValue(csd.defaultValue, true)}\n`;
1205
- text += `\tif options != nil && options.${naming.capitalize(param.name)} != nil {\n`;
1206
- text += `\t\t${defaultVar} = *options.${naming.capitalize(param.name)}\n`;
1207
- text += '}\n';
1466
+ let text = `${indent.get()}${defaultVar} := ${helpers.formatLiteralValue(csd.defaultValue, true)}\n`;
1467
+ text += `${indent.get()}if options != nil && options.${naming.capitalize(param.name)} != nil {\n`;
1468
+ text += `${indent.push().get()}${defaultVar} = *options.${naming.capitalize(param.name)}\n`;
1469
+ text += `${indent.pop().get()}}\n`;
1208
1470
  let serializedName;
1209
1471
  switch (param.kind) {
1210
1472
  case 'headerCollectionParam':
@@ -1254,17 +1516,17 @@ function isArrayOfDateTimeForMarshalling(paramType) {
1254
1516
  function needsResponseHandler(method) {
1255
1517
  return helpers.hasSchemaResponse(method) || method.returns.headers.length > 0;
1256
1518
  }
1257
- function generateResponseUnmarshaller(method, type, format, unmarshalTarget, imports) {
1519
+ function generateResponseUnmarshaller(method, type, format, unmarshalTarget, imports, indent) {
1258
1520
  let unmarshallerText = '';
1259
1521
  const zeroValue = getZeroReturnValue(method, 'handler');
1260
1522
  if (type.kind === 'time') {
1261
1523
  // use the designated time type for unmarshalling
1262
1524
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime');
1263
- unmarshallerText += `\tvar aux *datetime.${type.format}\n`;
1264
- unmarshallerText += `\tif err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1265
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1266
- unmarshallerText += '\t}\n';
1267
- unmarshallerText += `\tresult.${helpers.getResultFieldName(method)} = (*time.Time)(aux)\n`;
1525
+ unmarshallerText += `${indent.get()}var aux *datetime.${type.format}\n`;
1526
+ unmarshallerText += `${indent.get()}if err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1527
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1528
+ unmarshallerText += `${indent.pop().get()}}\n`;
1529
+ unmarshallerText += `${indent.get()}result.${helpers.getResultFieldName(method)} = (*time.Time)(aux)\n`;
1268
1530
  return unmarshallerText;
1269
1531
  }
1270
1532
  else if (isArrayOfDateTime(type)) {
@@ -1275,52 +1537,65 @@ function generateResponseUnmarshaller(method, type, format, unmarshalTarget, imp
1275
1537
  elementPtr = '';
1276
1538
  }
1277
1539
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime');
1278
- unmarshallerText += `\tvar aux []${elementPtr}datetime.${timeInfo?.format}\n`;
1279
- unmarshallerText += `\tif err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1280
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1281
- unmarshallerText += '\t}\n';
1282
- unmarshallerText += `\tcp := make([]${elementPtr}time.Time, len(aux))\n`;
1283
- unmarshallerText += '\tfor i := 0; i < len(aux); i++ {\n';
1284
- unmarshallerText += `\t\tcp[i] = (${elementPtr}time.Time)(aux[i])\n`;
1285
- unmarshallerText += '\t}\n';
1286
- unmarshallerText += `\tresult.${helpers.getResultFieldName(method)} = cp\n`;
1540
+ unmarshallerText += `${indent.get()}var aux []${elementPtr}datetime.${timeInfo?.format}\n`;
1541
+ unmarshallerText += `${indent.get()}if err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1542
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1543
+ unmarshallerText += `${indent.pop().get()}}\n`;
1544
+ unmarshallerText += `${indent.get()}cp := make([]${elementPtr}time.Time, len(aux))\n`;
1545
+ unmarshallerText += `${indent.get()}for i := 0; i < len(aux); i++ {\n`;
1546
+ unmarshallerText += `${indent.push().get()}cp[i] = (${elementPtr}time.Time)(aux[i])\n`;
1547
+ unmarshallerText += `${indent.pop().get()}}\n`;
1548
+ unmarshallerText += `${indent.get()}result.${helpers.getResultFieldName(method)} = cp\n`;
1287
1549
  return unmarshallerText;
1288
1550
  }
1289
1551
  else if (isMapOfDateTime(type)) {
1290
1552
  let timeType = isMapOfDateTime(type);
1291
1553
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime');
1292
- unmarshallerText += `\taux := map[string]*datetime.${timeType}{}\n`;
1293
- unmarshallerText += `\tif err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1294
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1295
- unmarshallerText += '\t}\n';
1296
- unmarshallerText += '\tcp := map[string]*time.Time{}\n';
1297
- unmarshallerText += '\tfor k, v := range aux {\n';
1298
- unmarshallerText += '\t\tcp[k] = (*time.Time)(v)\n';
1299
- unmarshallerText += '\t}\n';
1300
- unmarshallerText += `\tresult.${helpers.getResultFieldName(method)} = cp\n`;
1554
+ unmarshallerText += `${indent.get()}aux := map[string]*datetime.${timeType}{}\n`;
1555
+ unmarshallerText += `${indent.get()}if err := runtime.UnmarshalAs${format}(resp, &aux); err != nil {\n`;
1556
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1557
+ unmarshallerText += `${indent.pop().get()}}\n`;
1558
+ unmarshallerText += `${indent.get()}cp := map[string]*time.Time{}\n`;
1559
+ unmarshallerText += `${indent.get()}for k, v := range aux {\n`;
1560
+ unmarshallerText += `${indent.push().get()}cp[k] = (*time.Time)(v)\n`;
1561
+ unmarshallerText += `${indent.pop().get()}}\n`;
1562
+ unmarshallerText += `${indent.get()}result.${helpers.getResultFieldName(method)} = cp\n`;
1301
1563
  return unmarshallerText;
1302
1564
  }
1303
1565
  if (format === 'JSON' || format === 'XML') {
1304
1566
  if (type.kind === 'rawJSON') {
1305
- unmarshallerText += '\tbody, err := runtime.Payload(resp)\n';
1306
- unmarshallerText += '\tif err != nil {\n';
1307
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1308
- unmarshallerText += '\t}\n';
1309
- unmarshallerText += `\t${unmarshalTarget} = body\n`;
1567
+ unmarshallerText += `${indent.get()}body, err := runtime.Payload(resp)\n`;
1568
+ unmarshallerText += `${indent.get()}if err != nil {\n`;
1569
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1570
+ unmarshallerText += `${indent.pop().get()}}\n`;
1571
+ unmarshallerText += `${indent.get()}${unmarshalTarget} = body\n`;
1310
1572
  }
1311
1573
  else {
1312
- unmarshallerText += `\tif err := runtime.UnmarshalAs${getMediaFormat(type, format, `resp, &${unmarshalTarget}`)}; err != nil {\n`;
1313
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1314
- unmarshallerText += '\t}\n';
1574
+ unmarshallerText += `${indent.get()}if err := runtime.UnmarshalAs${getMediaFormat(type, format, `resp, &${unmarshalTarget}`)}; err != nil {\n`;
1575
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1576
+ unmarshallerText += `${indent.pop().get()}}\n`;
1315
1577
  }
1316
1578
  }
1317
1579
  else if (format === 'Text') {
1318
- unmarshallerText += '\tbody, err := runtime.Payload(resp)\n';
1319
- unmarshallerText += '\tif err != nil {\n';
1320
- unmarshallerText += `\t\treturn ${zeroValue}, err\n`;
1321
- unmarshallerText += '\t}\n';
1322
- unmarshallerText += '\ttxt := string(body)\n';
1323
- unmarshallerText += `\t${unmarshalTarget} = &txt\n`;
1580
+ unmarshallerText += `${indent.get()}body, err := runtime.Payload(resp)\n`;
1581
+ unmarshallerText += `${indent.get()}if err != nil {\n`;
1582
+ unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1583
+ unmarshallerText += `${indent.pop().get()}}\n`;
1584
+ let resultVar;
1585
+ switch (type.kind) {
1586
+ case 'scalar':
1587
+ resultVar = 'parsedBody';
1588
+ unmarshallerText += emitScalarParsing(type, 'string(body)', resultVar, imports, indent);
1589
+ unmarshallerText += `${indent.get()}${helpers.buildErrCheck(indent, 'err', zeroValue)}\n`;
1590
+ break;
1591
+ case 'string':
1592
+ resultVar = 'txt';
1593
+ unmarshallerText += `${indent.get()}${resultVar} := string(body)\n`;
1594
+ break;
1595
+ default:
1596
+ throw new CodegenError('UnsupportedTsp', `unsupported text return kind ${type.kind} for method ${method.receiver.type.name}.${method.name}`);
1597
+ }
1598
+ unmarshallerText += `${indent.get()}${unmarshalTarget} = &${resultVar}\n`;
1324
1599
  }
1325
1600
  else {
1326
1601
  // the remaining formats should have been handled elsewhere
@@ -1328,7 +1603,7 @@ function generateResponseUnmarshaller(method, type, format, unmarshalTarget, imp
1328
1603
  }
1329
1604
  return unmarshallerText;
1330
1605
  }
1331
- function createProtocolResponse(method, imports) {
1606
+ function createProtocolResponse(method, imports, indent) {
1332
1607
  if (!needsResponseHandler(method)) {
1333
1608
  return '';
1334
1609
  }
@@ -1337,70 +1612,70 @@ function createProtocolResponse(method, imports) {
1337
1612
  text += `func ${getClientReceiverDefinition(method.receiver)} ${name}(resp *http.Response) (${generateReturnsInfo(method, 'handler').join(', ')}) {\n`;
1338
1613
  const addHeaders = function (headers) {
1339
1614
  for (const header of headers) {
1340
- text += formatHeaderResponseValue(method, header, imports, 'result', `${method.returns.name}{}`);
1615
+ text += formatHeaderResponseValue(method, header, 'result', `${method.returns.name}{}`, imports, indent);
1341
1616
  }
1342
1617
  };
1343
1618
  const result = method.returns.result;
1344
1619
  if (!result) {
1345
1620
  // only headers
1346
- text += `\tresult := ${method.returns.name}{}\n`;
1621
+ text += `${indent.get()}result := ${method.returns.name}{}\n`;
1347
1622
  addHeaders(method.returns.headers);
1348
1623
  }
1349
1624
  else {
1350
1625
  switch (result.kind) {
1351
1626
  case 'anyResult':
1352
1627
  imports.add('fmt');
1353
- text += `\tresult := ${method.returns.name}{}\n`;
1628
+ text += `${indent.get()}result := ${method.returns.name}{}\n`;
1354
1629
  addHeaders(method.returns.headers);
1355
- text += '\tswitch resp.StatusCode {\n';
1630
+ text += `${indent.get()}switch resp.StatusCode {\n`;
1356
1631
  for (const statusCode of method.httpStatusCodes) {
1357
- text += `\tcase ${helpers.formatStatusCodes([statusCode])}:\n`;
1632
+ text += `${indent.get()}case ${helpers.formatStatusCodes([statusCode])}:\n`;
1358
1633
  const resultType = result.httpStatusCodeType[statusCode];
1359
1634
  if (!resultType) {
1360
1635
  // the operation contains a mix of schemas and non-schema responses
1361
1636
  continue;
1362
1637
  }
1363
- text += `\tvar val ${go.getTypeDeclaration(resultType, method.receiver.type.pkg)}\n`;
1364
- text += generateResponseUnmarshaller(method, resultType, result.format, 'val', imports);
1365
- text += '\tresult.Value = val\n';
1638
+ text += `${indent.get()}var val ${go.getTypeDeclaration(resultType, method.receiver.type.pkg)}\n`;
1639
+ text += generateResponseUnmarshaller(method, resultType, result.format, 'val', imports, indent);
1640
+ text += `${indent.get()}result.Value = val\n`;
1366
1641
  }
1367
- text += '\tdefault:\n';
1368
- text += `\t\treturn ${getZeroReturnValue(method, 'handler')}, fmt.Errorf("unhandled HTTP status code %d", resp.StatusCode)\n`;
1369
- text += '\t}\n';
1642
+ text += `${indent.get()}default:\n`;
1643
+ text += `${indent.push().get()}return ${getZeroReturnValue(method, 'handler')}, fmt.Errorf("unhandled HTTP status code %d", resp.StatusCode)\n`;
1644
+ text += `${indent.pop().get()}}\n`;
1370
1645
  break;
1371
1646
  case 'binaryResult':
1372
- text += `\tresult := ${method.returns.name}{${result.fieldName}: resp.Body}\n`;
1647
+ text += `${indent.get()}result := ${method.returns.name}{${result.fieldName}: resp.Body}\n`;
1373
1648
  addHeaders(method.returns.headers);
1374
1649
  break;
1375
1650
  case 'headAsBooleanResult':
1376
- text += `\tresult := ${method.returns.name}{${result.fieldName}: resp.StatusCode >= 200 && resp.StatusCode < 300}\n`;
1651
+ text += `${indent.get()}result := ${method.returns.name}{${result.fieldName}: resp.StatusCode >= 200 && resp.StatusCode < 300}\n`;
1377
1652
  addHeaders(method.returns.headers);
1378
1653
  break;
1379
1654
  case 'modelResult':
1380
- text += `\tresult := ${method.returns.name}{}\n`;
1655
+ text += `${indent.get()}result := ${method.returns.name}{}\n`;
1381
1656
  addHeaders(method.returns.headers);
1382
- text += generateResponseUnmarshaller(method, result.modelType, result.format, `result.${helpers.getResultFieldName(method)}`, imports);
1657
+ text += generateResponseUnmarshaller(method, result.modelType, result.format, `result.${helpers.getResultFieldName(method)}`, imports, indent);
1383
1658
  break;
1384
1659
  case 'monomorphicResult':
1385
- text += `\tresult := ${method.returns.name}{}\n`;
1660
+ text += `${indent.get()}result := ${method.returns.name}{}\n`;
1386
1661
  addHeaders(method.returns.headers);
1387
1662
  let target = `result.${helpers.getResultFieldName(method)}`;
1388
1663
  // when unmarshalling a wrapped XML array, unmarshal into the response envelope
1389
1664
  if (result.format === 'XML' && result.monomorphicType.kind === 'slice') {
1390
1665
  target = 'result';
1391
1666
  }
1392
- text += generateResponseUnmarshaller(method, result.monomorphicType, result.format, target, imports);
1667
+ text += generateResponseUnmarshaller(method, result.monomorphicType, result.format, target, imports, indent);
1393
1668
  break;
1394
1669
  case 'polymorphicResult':
1395
- text += `\tresult := ${method.returns.name}{}\n`;
1670
+ text += `${indent.get()}result := ${method.returns.name}{}\n`;
1396
1671
  addHeaders(method.returns.headers);
1397
- text += generateResponseUnmarshaller(method, result.interface, result.format, 'result', imports);
1672
+ text += generateResponseUnmarshaller(method, result.interface, result.format, 'result', imports, indent);
1398
1673
  break;
1399
1674
  default:
1400
1675
  result;
1401
1676
  }
1402
1677
  }
1403
- text += '\treturn result, nil\n';
1678
+ text += `${indent.get()}return result, nil\n`;
1404
1679
  text += '}\n\n';
1405
1680
  return text;
1406
1681
  }
@@ -1500,7 +1775,7 @@ function generateReturnsInfo(method, apiType) {
1500
1775
  }
1501
1776
  return [returnType, 'error'];
1502
1777
  }
1503
- function generateLROBeginMethod(method, imports, options) {
1778
+ function generateLROBeginMethod(method, options, imports, indent) {
1504
1779
  const params = getAPIParametersSig(method, imports);
1505
1780
  const returns = generateReturnsInfo(method, 'api');
1506
1781
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
@@ -1508,9 +1783,8 @@ function generateLROBeginMethod(method, imports, options) {
1508
1783
  if (method.docs.summary || method.docs.description) {
1509
1784
  text += helpers.formatDocCommentWithPrefix(fixUpMethodName(method), method.docs);
1510
1785
  text += genRespErrorDoc(method);
1511
- text += genApiVersionDoc(method.apiVersions);
1512
1786
  }
1513
- const zeroResp = getZeroReturnValue(method, 'api');
1787
+ const zeroResp = getZeroReturnValue(method, 'lro');
1514
1788
  const methodParams = helpers.getMethodParameters(method);
1515
1789
  for (const param of methodParams) {
1516
1790
  text += helpers.formatCommentAsBulletItem(param.name, param.docs);
@@ -1522,16 +1796,17 @@ function generateLROBeginMethod(method, imports, options) {
1522
1796
  // for paged LROs, we construct a pager and pass it to the LRO ctor.
1523
1797
  pollerTypeParam = `[*runtime.Pager${pollerTypeParam}]`;
1524
1798
  pollerType = '&pager';
1525
- text += '\tpager := ';
1526
- text += emitPagerDefinition(method, imports, options);
1799
+ text += `${indent.get()}pager := `;
1800
+ text += emitPagerDefinition(method, options, imports, indent);
1527
1801
  }
1528
- text += '\tif options == nil || options.ResumeToken == "" {\n';
1802
+ text += `${indent.get()}if options == nil || options.ResumeToken == "" {\n`;
1803
+ indent.push();
1529
1804
  // creating the poller from response branch
1530
1805
  const opName = method.naming.internalMethod;
1531
- text += `\t\tresp, err := client.${opName}(${helpers.getCreateRequestParameters(method)})\n`;
1532
- text += '\t\tif err != nil {\n';
1533
- text += `\t\t\treturn ${zeroResp}, err\n`;
1534
- text += '\t\t}\n';
1806
+ text += `${indent.get()}resp, err := client.${opName}(${helpers.getCreateRequestParameters(method)})\n`;
1807
+ text += `${indent.get()}if err != nil {\n`;
1808
+ text += `${indent.push().get()}return ${zeroResp}, err\n`;
1809
+ text += `${indent.pop().get()}}\n`;
1535
1810
  let finalStateVia = '';
1536
1811
  // LRO operation might have a special configuration set in x-ms-long-running-operation-options
1537
1812
  // which indicates a specific url to perform the final Get operation on
@@ -1553,7 +1828,7 @@ function generateLROBeginMethod(method, imports, options) {
1553
1828
  throw new CodegenError('InternalError', `unhandled final-state-via value ${finalStateVia}`);
1554
1829
  }
1555
1830
  }
1556
- text += '\t\tpoller, err := runtime.NewPoller';
1831
+ text += `${indent.get()}poller, err := runtime.NewPoller`;
1557
1832
  if (finalStateVia === '' && pollerType === 'nil' && !options.injectSpans) {
1558
1833
  // the generic type param is redundant when it's also specified in the
1559
1834
  // options struct so we only include it when there's no options.
@@ -1566,25 +1841,29 @@ function generateLROBeginMethod(method, imports, options) {
1566
1841
  }
1567
1842
  else {
1568
1843
  // at least one option
1844
+ indent.push();
1569
1845
  text += `&runtime.NewPollerOptions${pollerTypeParam}{\n`;
1570
1846
  if (finalStateVia !== '') {
1571
- text += `\t\t\tFinalStateVia: ${finalStateVia},\n`;
1847
+ text += `${indent.get()}FinalStateVia: ${finalStateVia},\n`;
1572
1848
  }
1573
1849
  if (method.operationLocationResultPath) {
1574
- text += `\t\t\tOperationLocationResultPath: "${method.operationLocationResultPath}",\n`;
1850
+ text += `${indent.get()}OperationLocationResultPath: "${method.operationLocationResultPath}",\n`;
1575
1851
  }
1576
1852
  if (pollerType !== 'nil') {
1577
- text += `\t\t\tResponse: ${pollerType},\n`;
1853
+ text += `${indent.get()}Response: ${pollerType},\n`;
1578
1854
  }
1579
1855
  if (options.injectSpans) {
1580
- text += '\t\t\tTracer: client.internal.Tracer(),\n';
1856
+ text += `${indent.get()}Tracer: client.internal.Tracer(),\n`;
1581
1857
  }
1582
- text += '\t\t})\n';
1858
+ indent.pop();
1859
+ text += `${indent.get()}})\n`;
1583
1860
  }
1584
- text += '\t\treturn poller, err\n';
1585
- text += '\t} else {\n';
1861
+ text += `${indent.get()}return poller, err\n`;
1862
+ indent.pop();
1863
+ text += `${indent.get()}} else {\n`;
1864
+ indent.push();
1586
1865
  // creating the poller from resume token branch
1587
- text += '\t\treturn runtime.NewPollerFromResumeToken';
1866
+ text += `${indent.get()}return runtime.NewPollerFromResumeToken`;
1588
1867
  if (pollerType === 'nil' && !options.injectSpans) {
1589
1868
  text += pollerTypeParam;
1590
1869
  }
@@ -1593,16 +1872,19 @@ function generateLROBeginMethod(method, imports, options) {
1593
1872
  text += 'nil)\n';
1594
1873
  }
1595
1874
  else {
1875
+ indent.push();
1596
1876
  text += `&runtime.NewPollerFromResumeTokenOptions${pollerTypeParam}{\n`;
1597
1877
  if (pollerType !== 'nil') {
1598
- text += `\t\t\tResponse: ${pollerType},\n`;
1878
+ text += `${indent.get()}Response: ${pollerType},\n`;
1599
1879
  }
1600
1880
  if (options.injectSpans) {
1601
- text += '\t\t\tTracer: client.internal.Tracer(),\n';
1881
+ text += `${indent.get()}Tracer: client.internal.Tracer(),\n`;
1602
1882
  }
1603
- text += '\t\t})\n';
1883
+ indent.pop();
1884
+ text += `${indent.get()}})\n`;
1604
1885
  }
1605
- text += '\t}\n';
1886
+ indent.pop();
1887
+ text += `${indent.get()}}\n`;
1606
1888
  text += '}\n\n';
1607
1889
  return text;
1608
1890
  }