@azure-tools/typespec-rust 0.36.0 → 0.38.0

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 (76) hide show
  1. package/dist/src/codegen/clients.d.ts +4 -4
  2. package/dist/src/codegen/clients.d.ts.map +1 -1
  3. package/dist/src/codegen/clients.js +208 -88
  4. package/dist/src/codegen/clients.js.map +1 -1
  5. package/dist/src/codegen/codeGenerator.d.ts +6 -0
  6. package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
  7. package/dist/src/codegen/codeGenerator.js +112 -32
  8. package/dist/src/codegen/codeGenerator.js.map +1 -1
  9. package/dist/src/codegen/context.d.ts +10 -0
  10. package/dist/src/codegen/context.d.ts.map +1 -1
  11. package/dist/src/codegen/context.js +57 -15
  12. package/dist/src/codegen/context.js.map +1 -1
  13. package/dist/src/codegen/docTests.d.ts +1 -2
  14. package/dist/src/codegen/docTests.d.ts.map +1 -1
  15. package/dist/src/codegen/docTests.js +13 -3
  16. package/dist/src/codegen/docTests.js.map +1 -1
  17. package/dist/src/codegen/enums.d.ts +4 -4
  18. package/dist/src/codegen/enums.d.ts.map +1 -1
  19. package/dist/src/codegen/enums.js +21 -21
  20. package/dist/src/codegen/enums.js.map +1 -1
  21. package/dist/src/codegen/headerTraits.d.ts +2 -2
  22. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  23. package/dist/src/codegen/headerTraits.js +17 -9
  24. package/dist/src/codegen/headerTraits.js.map +1 -1
  25. package/dist/src/codegen/helpers.d.ts +12 -3
  26. package/dist/src/codegen/helpers.d.ts.map +1 -1
  27. package/dist/src/codegen/helpers.js +22 -13
  28. package/dist/src/codegen/helpers.js.map +1 -1
  29. package/dist/src/codegen/lib.d.ts +3 -1
  30. package/dist/src/codegen/lib.d.ts.map +1 -1
  31. package/dist/src/codegen/lib.js +5 -1
  32. package/dist/src/codegen/lib.js.map +1 -1
  33. package/dist/src/codegen/mod.d.ts +9 -2
  34. package/dist/src/codegen/mod.d.ts.map +1 -1
  35. package/dist/src/codegen/mod.js +25 -9
  36. package/dist/src/codegen/mod.js.map +1 -1
  37. package/dist/src/codegen/models.d.ts +4 -4
  38. package/dist/src/codegen/models.d.ts.map +1 -1
  39. package/dist/src/codegen/models.js +96 -39
  40. package/dist/src/codegen/models.js.map +1 -1
  41. package/dist/src/codegen/unions.d.ts +4 -4
  42. package/dist/src/codegen/unions.d.ts.map +1 -1
  43. package/dist/src/codegen/unions.js +81 -62
  44. package/dist/src/codegen/unions.js.map +1 -1
  45. package/dist/src/codegen/use.d.ts +4 -2
  46. package/dist/src/codegen/use.d.ts.map +1 -1
  47. package/dist/src/codegen/use.js +22 -26
  48. package/dist/src/codegen/use.js.map +1 -1
  49. package/dist/src/codemodel/client.d.ts +32 -16
  50. package/dist/src/codemodel/client.d.ts.map +1 -1
  51. package/dist/src/codemodel/client.js +11 -7
  52. package/dist/src/codemodel/client.js.map +1 -1
  53. package/dist/src/codemodel/crate.d.ts +42 -12
  54. package/dist/src/codemodel/crate.d.ts.map +1 -1
  55. package/dist/src/codemodel/crate.js +21 -46
  56. package/dist/src/codemodel/crate.js.map +1 -1
  57. package/dist/src/codemodel/types.d.ts +69 -38
  58. package/dist/src/codemodel/types.d.ts.map +1 -1
  59. package/dist/src/codemodel/types.js +31 -10
  60. package/dist/src/codemodel/types.js.map +1 -1
  61. package/dist/src/emitter.d.ts.map +1 -1
  62. package/dist/src/emitter.js +40 -12
  63. package/dist/src/emitter.js.map +1 -1
  64. package/dist/src/lib.d.ts +2 -0
  65. package/dist/src/lib.d.ts.map +1 -1
  66. package/dist/src/lib.js +6 -0
  67. package/dist/src/lib.js.map +1 -1
  68. package/dist/src/tcgcadapter/adapter.d.ts +76 -13
  69. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  70. package/dist/src/tcgcadapter/adapter.js +777 -190
  71. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  72. package/dist/src/utils/utils.d.ts +23 -4
  73. package/dist/src/utils/utils.d.ts.map +1 -1
  74. package/dist/src/utils/utils.js +54 -23
  75. package/dist/src/utils/utils.js.map +1 -1
  76. package/package.json +17 -17
@@ -5,13 +5,13 @@ export interface ClientModules {
5
5
  /** the list of client modules */
6
6
  modules: Array<helpers.Module>;
7
7
  /** the client method options module */
8
- options: helpers.Module;
8
+ options?: helpers.Module;
9
9
  }
10
10
  /**
11
11
  * emits the content for all client files
12
12
  *
13
- * @param crate the crate for which to emit clients
14
- * @returns client content or undefined if the crate contains no clients
13
+ * @param module the module for which to emit clients
14
+ * @returns client content or undefined if the module contains no clients
15
15
  */
16
- export declare function emitClients(crate: rust.Crate): ClientModules | undefined;
16
+ export declare function emitClients(module: rust.ModuleContainer): ClientModules | undefined;
17
17
  //# sourceMappingURL=clients.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAG9C,yBAAyB;AACzB,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,aAAa,GAAG,SAAS,CA+RxE"}
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAG9C,yBAAyB;AACzB,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,GAAG,aAAa,GAAG,SAAS,CA+UnF"}
@@ -13,11 +13,11 @@ import * as utils from '../utils/utils.js';
13
13
  /**
14
14
  * emits the content for all client files
15
15
  *
16
- * @param crate the crate for which to emit clients
17
- * @returns client content or undefined if the crate contains no clients
16
+ * @param module the module for which to emit clients
17
+ * @returns client content or undefined if the module contains no clients
18
18
  */
19
- export function emitClients(crate) {
20
- if (crate.clients.length === 0) {
19
+ export function emitClients(module) {
20
+ if (module.clients.length === 0) {
21
21
  return undefined;
22
22
  }
23
23
  // returns true if the client options type needs to explicitly implement Default
@@ -34,8 +34,8 @@ export function emitClients(crate) {
34
34
  };
35
35
  const clientModules = new Array();
36
36
  // emit the clients, one file per client
37
- for (const client of crate.clients) {
38
- const use = new Use('clients');
37
+ for (const client of module.clients) {
38
+ const use = new Use(module, 'clients');
39
39
  const indent = new helpers.indentation();
40
40
  let body = helpers.formatDocComment(client.docs);
41
41
  use.add('azure_core', 'tracing');
@@ -163,6 +163,7 @@ export function emitClients(crate) {
163
163
  body += `${indent.get()}pub fn endpoint(&self) -> &Url {\n`;
164
164
  body += `${indent.push().get()}&self.${client.endpoint.name}\n`;
165
165
  body += `${indent.pop().get()}}\n\n`;
166
+ const crate = helpers.getCrate(module);
166
167
  for (let i = 0; i < client.methods.length; ++i) {
167
168
  const method = client.methods[i];
168
169
  const returnType = helpers.getTypeDeclaration(method.returns);
@@ -231,6 +232,42 @@ export function emitClients(crate) {
231
232
  }
232
233
  }
233
234
  body += '}\n\n'; // end client impl
235
+ // Emit pub(crate) const declarations for fields with default value constants.
236
+ //
237
+ // These constants are ALWAYS emitted, even when the options type is suppressed.
238
+ // In SDK crates with suppressed options, SDK authors need these constants to
239
+ // reference TypeSpec-defined default values (e.g., api-version) in their
240
+ // hand-authored Default impl. Suppressing them would force hardcoding.
241
+ //
242
+ // Visibility design:
243
+ // - Non-suppressed: constant is referenced by the generated Default impl, so
244
+ // it's alive and no dead_code suppression is needed.
245
+ // - Suppressed: the generated options type doesn't exist — SDK authors provide
246
+ // their own. The constant is a convenience they SHOULD use, but may not.
247
+ // #[allow(dead_code)] is appropriate here (parallels std library patterns).
248
+ if (client.constructable) {
249
+ const isSuppressed = client.constructable.suppressed === 'yes';
250
+ for (const field of client.constructable.options.type.fields) {
251
+ if (field.defaultValueConstant) {
252
+ if (isSuppressed) {
253
+ // Plain text doc comment — the options type doesn't exist when suppressed,
254
+ // so intra-doc links would be broken.
255
+ body += `/// Default value for \`${client.constructable.options.type.name}::${field.name}\`.\n`;
256
+ body += `///\n`;
257
+ body += `/// This constant is available for SDK authors to use in hand-authored code.\n`;
258
+ body += `/// When the options type is suppressed (via \`@access(Access.internal)\`), the\n`;
259
+ body += `/// SDK author provides a custom options type and should reference this constant\n`;
260
+ body += `/// in their \`Default\` implementation rather than hardcoding the value.\n`;
261
+ body += `#[allow(dead_code)]\n`;
262
+ }
263
+ else {
264
+ // Intra-doc link — the options type exists and the link resolves.
265
+ body += `/// Default value for [\`${client.constructable.options.type.name}::${field.name}\`].\n`;
266
+ }
267
+ body += `pub(crate) const ${field.defaultValueConstant.name}: &str = "${field.defaultValueConstant.value}";\n\n`;
268
+ }
269
+ }
270
+ }
234
271
  // only implement Default when there's more than one field.
235
272
  // for the single-case field we just derive Default.
236
273
  if (client.constructable && clientOptionsImplDefault(client.constructable)) {
@@ -256,7 +293,16 @@ export function emitClients(crate) {
256
293
  // add using for method_options as required
257
294
  for (const method of client.methods) {
258
295
  if (method.kind !== 'clientaccessor') {
259
- use.add('crate::generated::models', method.options.type.name);
296
+ // client method options types are always in the same module as their client method
297
+ use.add(`${utils.buildImportPath(client.module, client.module)}::models`, method.options.type.type.name);
298
+ // add imports for parameter group types
299
+ const seenGroups = new Set();
300
+ for (const param of method.params) {
301
+ if (param.group && !seenGroups.has(param.group.type.name)) {
302
+ seenGroups.add(param.group.type.name);
303
+ use.add(`${utils.buildImportPath(client.module, client.module)}::models`, param.group.type.name);
304
+ }
305
+ }
260
306
  }
261
307
  }
262
308
  let content = helpers.contentPreamble();
@@ -271,64 +317,102 @@ export function emitClients(crate) {
271
317
  }
272
318
  return {
273
319
  modules: clientModules,
274
- options: getMethodOptions(crate),
320
+ options: getMethodOptions(module),
275
321
  };
276
322
  }
277
- function getMethodOptions(crate) {
278
- const use = new Use('modelsOther');
323
+ function getMethodOptions(module) {
324
+ const use = new Use(module, 'modelsOther');
279
325
  const indent = new helpers.indentation();
280
326
  const visTracker = new helpers.VisibilityTracker();
281
- let body = '';
282
- for (const client of crate.clients) {
283
- for (let i = 0; i < client.methods.length; ++i) {
284
- const method = client.methods[i];
327
+ // collect all struct blocks so they can be sorted by name
328
+ const structBlocks = [];
329
+ for (const client of module.clients) {
330
+ for (const method of client.methods) {
285
331
  if (method.kind === 'clientaccessor') {
286
332
  continue;
287
333
  }
288
- body += helpers.formatDocComment(method.options.type.docs);
334
+ // method options struct
335
+ let block = '';
336
+ const optionsStruct = method.options.type.type;
337
+ block += helpers.formatDocComment(optionsStruct.docs);
289
338
  use.add('azure_core::fmt', 'SafeDebug');
290
- body += '#[derive(Clone, Default, SafeDebug)]\n';
291
- body += `${helpers.emitVisibility(method.options.type.visibility)}struct ${helpers.getTypeDeclaration(method.options.type)} {\n`;
292
- visTracker.update(method.options.type.visibility);
293
- for (let i = 0; i < method.options.type.fields.length; ++i) {
294
- const field = method.options.type.fields[i];
339
+ block += '#[derive(Clone, Default, SafeDebug)]\n';
340
+ block += `${helpers.emitVisibility(optionsStruct.visibility)}struct ${helpers.getTypeDeclaration(optionsStruct)} {\n`;
341
+ visTracker.update(optionsStruct.visibility);
342
+ for (let i = 0; i < optionsStruct.fields.length; ++i) {
343
+ const field = optionsStruct.fields[i];
295
344
  use.addForType(field.type);
296
345
  const fieldDocs = helpers.formatDocComment(field.docs);
297
346
  if (fieldDocs.length > 0) {
298
- body += `${indent.get()}${fieldDocs}`;
347
+ block += `${indent.get()}${fieldDocs}`;
299
348
  }
300
- body += `${indent.get()}${helpers.emitVisibility(method.visibility)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
301
- if (i + 1 < method.options.type.fields.length) {
302
- body += '\n';
349
+ block += `${indent.get()}${helpers.emitVisibility(method.visibility)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
350
+ if (i + 1 < optionsStruct.fields.length) {
351
+ block += '\n';
303
352
  }
304
353
  }
305
- body += '}\n\n'; // end options
354
+ block += '}\n';
306
355
  if (method.kind === 'pageable' || method.kind === 'lro') {
307
- body += `impl ${helpers.getTypeDeclaration(method.options.type, 'anonymous')} {\n`;
308
- const wrappedTypeName = helpers.wrapInBackTicks(helpers.getTypeDeclaration(method.options.type, 'omit'));
309
- body += `${indent.get()}/// Transforms this [${wrappedTypeName}] into a new ${wrappedTypeName} that owns the underlying data, cloning it if necessary.\n`;
310
- body += `${indent.get()}pub fn into_owned(self) -> ${method.options.type.name}<'static> {\n`;
311
- body += `${indent.push().get()}${method.options.type.name} {\n`;
356
+ block += '\n';
357
+ block += `impl ${helpers.getTypeDeclaration(optionsStruct, 'anonymous')} {\n`;
358
+ const wrappedTypeName = helpers.wrapInBackTicks(helpers.getTypeDeclaration(optionsStruct, 'omit'));
359
+ block += `${indent.get()}/// Transforms this [${wrappedTypeName}] into a new ${wrappedTypeName} that owns the underlying data, cloning it if necessary.\n`;
360
+ block += `${indent.get()}pub fn into_owned(self) -> ${optionsStruct.name}<'static> {\n`;
361
+ block += `${indent.push().get()}${optionsStruct.name} {\n`;
312
362
  indent.push();
313
- for (const field of method.options.type.fields) {
363
+ for (const field of optionsStruct.fields) {
314
364
  if (field.type.kind === 'clientMethodOptions' || field.type.kind === 'pagerOptions' || field.type.kind === 'pollerOptions') {
315
- body += `${indent.get()}${field.name}: ${field.type.name} {\n`;
316
- body += `${indent.push().get()}context: self.${field.name}.context.into_owned(),\n`;
317
- body += `${indent.get()}..self.${field.name}\n`;
318
- body += `${indent.pop().get()}},\n`;
365
+ block += `${indent.get()}${field.name}: ${field.type.name} {\n`;
366
+ block += `${indent.push().get()}context: self.${field.name}.context.into_owned(),\n`;
367
+ block += `${indent.get()}..self.${field.name}\n`;
368
+ block += `${indent.pop().get()}},\n`;
319
369
  continue;
320
370
  }
321
- body += `${indent.get()}${field.name}: self.${field.name},\n`;
371
+ block += `${indent.get()}${field.name}: self.${field.name},\n`;
322
372
  }
323
- body += `${indent.pop().get()}}\n`;
324
- body += `${indent.pop().get()}}\n`;
325
- body += '}\n';
373
+ block += `${indent.pop().get()}}\n`;
374
+ block += `${indent.pop().get()}}\n`;
375
+ block += '}\n';
326
376
  }
327
- if (i + 1 < client.methods.length) {
328
- body += '\n';
377
+ structBlocks.push({ name: optionsStruct.name, body: block });
378
+ // parameter group structs
379
+ const seenGroups = new Set();
380
+ for (const param of method.params) {
381
+ if (param.group) {
382
+ seenGroups.add(param.group);
383
+ }
384
+ }
385
+ for (const group of seenGroups) {
386
+ const groupParams = method.params.filter(p => p.group === group);
387
+ let groupBlock = '';
388
+ groupBlock += helpers.formatDocComment(group.type.docs);
389
+ use.add('azure_core::fmt', 'SafeDebug');
390
+ groupBlock += '#[derive(Clone, SafeDebug)]\n';
391
+ groupBlock += `${helpers.emitVisibility(group.type.visibility)}struct ${group.type.name}${group.type.lifetime ? `<${group.type.lifetime.name}>` : ''} {\n`;
392
+ visTracker.update(group.type.visibility);
393
+ for (let i = 0; i < groupParams.length; i++) {
394
+ const field = groupParams[i];
395
+ use.addForType(field.type);
396
+ const fieldDocs = helpers.formatDocComment(field.docs);
397
+ if (fieldDocs.length > 0) {
398
+ if (i > 0) {
399
+ groupBlock += '\n';
400
+ }
401
+ groupBlock += `${indent.get()}${fieldDocs}`;
402
+ }
403
+ groupBlock += `${indent.get()}${helpers.emitVisibility(group.type.visibility)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
404
+ }
405
+ groupBlock += '}\n';
406
+ structBlocks.push({ name: group.type.name, body: groupBlock });
329
407
  }
330
408
  }
331
409
  }
410
+ structBlocks.sort((a, b) => helpers.sortAscending(a.name, b.name));
411
+ const body = structBlocks.map((b) => b.body).join('\n');
412
+ if (body === '') {
413
+ // client is top-level only, no methods just accessors
414
+ return undefined;
415
+ }
332
416
  let content = helpers.contentPreamble();
333
417
  content += use.text();
334
418
  content += body;
@@ -351,7 +435,17 @@ function getParamsBlockDocComment(indent, callable) {
351
435
  return `* ${helpers.wrapInBackTicks(paramName)} - `;
352
436
  };
353
437
  let paramsContent = '';
438
+ const documentedGroups = new Set();
354
439
  for (const param of callable.params) {
440
+ if ('group' in param && param.group) {
441
+ // required parameter group appears as a single param in the method sig
442
+ const group = param.group;
443
+ if (!documentedGroups.has(group.name)) {
444
+ documentedGroups.add(group.name);
445
+ paramsContent += helpers.formatDocComment(group.docs, false, formatParamBullet(group.name), indent);
446
+ }
447
+ continue;
448
+ }
355
449
  let optional = false;
356
450
  if ('optional' in param) {
357
451
  optional = param.optional;
@@ -426,7 +520,17 @@ function getMethodParamsCountAndSig(method, use) {
426
520
  }
427
521
  }
428
522
  else {
523
+ const emittedGroups = new Set();
429
524
  for (const param of method.params) {
525
+ if (param.group) {
526
+ if (!emittedGroups.has(param.group.name)) {
527
+ emittedGroups.add(param.group.name);
528
+ // required parameter group appears as a single struct parameter in the method signature
529
+ paramsSig.push(`${param.group.name}: ${helpers.getTypeDeclaration(param.group.type, 'anonymous')}`);
530
+ ++count;
531
+ }
532
+ continue;
533
+ }
430
534
  const paramType = helpers.unwrapType(param.type);
431
535
  if (paramType.kind === 'literal') {
432
536
  // literal params are embedded directly in the code (e.g. accept header param)
@@ -444,7 +548,7 @@ function getMethodParamsCountAndSig(method, use) {
444
548
  ++count;
445
549
  }
446
550
  }
447
- paramsSig.push(`options: ${helpers.getTypeDeclaration(method.options, 'anonymous')}`);
551
+ paramsSig.push(`${method.options.name}: ${helpers.getTypeDeclaration(method.options.type, 'anonymous')}`);
448
552
  ++count;
449
553
  }
450
554
  return { count: count, sig: paramsSig.join(', ') };
@@ -453,11 +557,11 @@ function getMethodParamsCountAndSig(method, use) {
453
557
  * returns documentation for header trait access if the method has response headers.
454
558
  *
455
559
  * @param indent the current indentation level
456
- * @param crate the crate to which method belongs
560
+ * @param module the module to which method belongs
457
561
  * @param method the method for which to generate header trait documentation
458
562
  * @returns the header trait documentation or empty string if not applicable
459
563
  */
460
- function getHeaderTraitDocComment(indent, crate, method) {
564
+ function getHeaderTraitDocComment(indent, module, method) {
461
565
  if (!method.responseHeaders) {
462
566
  return '';
463
567
  }
@@ -479,15 +583,15 @@ function getHeaderTraitDocComment(indent, crate, method) {
479
583
  headerDocs += `${indent.get()}/// The returned [${helpers.wrapInBackTicks(returnType)}](azure_core::http::${returnType}) implements the [${helpers.wrapInBackTicks(traitName)}] trait, which provides\n`;
480
584
  headerDocs += `${indent.get()}/// access to response headers. For example:\n`;
481
585
  headerDocs += `${indent.get()}///\n`;
482
- headerDocs += emitHeaderTraitDocExample(crate.name, method.responseHeaders, indent);
586
+ headerDocs += emitHeaderTraitDocExample(method.responseHeaders, indent);
483
587
  headerDocs += `${indent.get()}///\n`;
484
588
  headerDocs += `${indent.get()}/// ### Available headers\n`;
485
589
  // List all available headers
486
590
  for (const header of method.responseHeaders.headers) {
487
- headerDocs += `${indent.get()}/// * [${helpers.wrapInBackTicks(header.name)}()](crate::generated::models::${traitName}::${header.name}) - ${header.header}\n`;
591
+ headerDocs += `${indent.get()}/// * [${helpers.wrapInBackTicks(header.name)}()](${utils.buildImportPath(module, module)}::models::${traitName}::${header.name}) - ${header.header}\n`;
488
592
  }
489
593
  headerDocs += `${indent.get()}///\n`;
490
- headerDocs += `${indent.get()}/// [${helpers.wrapInBackTicks(traitName)}]: crate::generated::models::${traitName}\n`;
594
+ headerDocs += `${indent.get()}/// [${helpers.wrapInBackTicks(traitName)}]: ${utils.buildImportPath(module, module)}::models::${traitName}\n`;
491
595
  return headerDocs;
492
596
  }
493
597
  /**
@@ -565,6 +669,17 @@ function getClientAccessorMethodBody(indent, client, clientAccessor) {
565
669
  if (clientAccessor.returns.fields.find((e) => e.name === field.name)) {
566
670
  initFields.push(`${field.name}: self.${field.name}${nonCopyableType(field.type) ? '.clone()' : ''}`);
567
671
  }
672
+ else {
673
+ // by convention the fields propagated from parent to child have the same name.
674
+ // there is an edge-case though for multi-client where the parent client contains
675
+ // multiple api-version params, one per child client. in this case, the param
676
+ // names include the client name as a prefix to disambiguate them. we check for
677
+ // that case here.
678
+ const disambiguatedField = clientAccessor.returns.fields.find((e) => utils.deduplicateClientFieldName(clientAccessor.returns, e.name) === field.name);
679
+ if (disambiguatedField) {
680
+ initFields.push(`${disambiguatedField.name}: self.${field.name}${nonCopyableType(field.type) ? '.clone()' : ''}`);
681
+ }
682
+ }
568
683
  }
569
684
  // sort the fields as the fields in the client are also sorted
570
685
  initFields.sort();
@@ -683,8 +798,8 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
683
798
  throw new CodegenError('InternalError', 'too many HTTP path chunks');
684
799
  }
685
800
  let body = '';
686
- // if path is just "/" no need to set it again, we're already there
687
- if (pathChunks[0] !== '/') {
801
+ // if path is just "/" or empty, no need to set it again, we're already there
802
+ if (pathChunks[0] !== '/' && pathChunks[0] !== '') {
688
803
  use.add('azure_core::http', 'UrlExt');
689
804
  let path = `"${pathChunks[0]}"`;
690
805
  if (paramGroups.path.length === 0) {
@@ -704,50 +819,49 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
704
819
  let wrapSortedVec = (s) => s;
705
820
  let paramExpression;
706
821
  if (pathParam.kind === 'pathHashMap') {
707
- wrapSortedVec = (s) => `${indent.get()}{`
708
- + `${indent.push().get()}let mut ${pathParam.name}_vec = ${pathParam.name}.iter().collect::<Vec<_>>();\n`
709
- + `${indent.get()}${pathParam.name}_vec.sort_by_key(|p| p.0);\n`
710
- + `${s}`
711
- + `${indent.pop().get()}}`;
822
+ const pathParamRef = qualifiedParamName(pathParam);
823
+ wrapSortedVec = (s) => `${indent.get()}let mut ${pathParam.name} = ${pathParamRef}.iter().collect::<Vec<_>>();\n`
824
+ + `${indent.get()}${pathParam.name}.sort_by_key(|p| p.0);\n${s}`;
712
825
  const kEqualsV = '"{k}={v}"';
713
826
  const kCommaV = '"{k},{v}"';
714
- paramExpression = `&${pathParam.name}_vec.iter().map(|(k,v)| `
827
+ paramExpression = `&${pathParam.name}.iter().map(|(k,v)| `
715
828
  + (pathParam.explode
716
829
  ? `format!(${kEqualsV})).collect::<Vec<_>>().join(",")`
717
830
  : `format!(${kCommaV})).collect::<Vec<_>>().join(",")`);
718
831
  switch (pathParam.style) {
719
832
  case 'path':
720
- paramExpression = `&format!("/{}", ${pathParam.name}_vec.iter().map(|(k,v)| `
833
+ paramExpression = `&format!("/{}", ${pathParam.name}.iter().map(|(k,v)| `
721
834
  + (pathParam.explode
722
835
  ? `format!(${kEqualsV})).collect::<Vec<_>>().join("/"))`
723
836
  : `format!(${kCommaV})).collect::<Vec<_>>().join(","))`);
724
837
  break;
725
838
  case 'label':
726
- paramExpression = `&format!(".{}", ${pathParam.name}_vec.iter().map(|(k,v)| `
839
+ paramExpression = `&format!(".{}", ${pathParam.name}.iter().map(|(k,v)| `
727
840
  + (pathParam.explode
728
841
  ? `format!(${kEqualsV})).collect::<Vec<_>>().join("."))`
729
842
  : `format!(${kCommaV})).collect::<Vec<_>>().join(","))`);
730
843
  break;
731
844
  case 'matrix':
732
845
  paramExpression = pathParam.explode
733
- ? (`&format!(";{}", ${pathParam.name}_vec.into_iter().map(|(k,v)| `
846
+ ? (`&format!(";{}", ${pathParam.name}.into_iter().map(|(k,v)| `
734
847
  + `format!(${kEqualsV})).collect::<Vec<_>>().join(";"))`)
735
- : (`&format!(";${pathParam.name}={}", ${pathParam.name}_vec.into_iter().map(|(k,v)| `
848
+ : (`&format!(";${pathParam.name}={}", ${pathParam.name}.into_iter().map(|(k,v)| `
736
849
  + `format!(${kCommaV})).collect::<Vec<_>>().join(","))`);
737
850
  break;
738
851
  }
739
852
  }
740
853
  else if (pathParam.kind === 'pathCollection') {
741
- paramExpression = `&${pathParam.name}.join(",")`;
854
+ const pathParamRef = qualifiedParamName(pathParam);
855
+ paramExpression = `&${pathParamRef}.join(",")`;
742
856
  switch (pathParam.style) {
743
857
  case 'path':
744
- paramExpression = `&format!("/{}", ${pathParam.name}.join("${pathParam.explode ? '/' : ','}"))`;
858
+ paramExpression = `&format!("/{}", ${pathParamRef}.join("${pathParam.explode ? '/' : ','}"))`;
745
859
  break;
746
860
  case 'label':
747
- paramExpression = `&format!(".{}", ${pathParam.name}.join("${pathParam.explode ? '.' : ','}"))`;
861
+ paramExpression = `&format!(".{}", ${pathParamRef}.join("${pathParam.explode ? '.' : ','}"))`;
748
862
  break;
749
863
  case 'matrix':
750
- paramExpression = `&format!(";${pathParam.name}={}", ${pathParam.name}.join(`
864
+ paramExpression = `&format!(";${pathParam.name}={}", ${pathParamRef}.join(`
751
865
  + `"${pathParam.explode ? `;${pathParam.name}=` : ','}"))`;
752
866
  break;
753
867
  }
@@ -822,8 +936,9 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
822
936
  for (const queryParam of paramGroups.query) {
823
937
  if (queryParam.kind === 'queryCollection' && queryParam.format === 'multi') {
824
938
  body += getParamValueHelper(indent, queryParam, () => {
939
+ const queryParamRef = qualifiedParamName(queryParam);
825
940
  const valueVar = queryParam.name[0];
826
- let text = `${indent.get()}for ${valueVar} in ${queryParam.name}.iter() {\n`;
941
+ let text = `${indent.get()}for ${valueVar} in ${queryParamRef}.iter() {\n`;
827
942
  // if queryParam is a &[&str] then we'll need to deref the iterator
828
943
  const deref = utils.asTypeOf(queryParam.type, 'str', 'ref', 'slice', 'ref') ? '*' : '';
829
944
  text += `${indent.push().get()}query_builder.append_pair("${queryParam.key}", ${deref}${getHeaderPathQueryParamValue(use, queryParam, !queryParam.optional, false, valueVar)});\n`;
@@ -833,19 +948,14 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
833
948
  }
834
949
  else if (queryParam.kind === 'queryHashMap') {
835
950
  body += getParamValueHelper(indent, queryParam, () => {
836
- let text = `${indent.get()}{\n`;
837
- text += `${indent.push().get()}let mut ${queryParam.name}_vec = ${queryParam.name}.iter().collect::<Vec<_>>();\n`;
838
- text += `${indent.get()}${queryParam.name}_vec.sort_by_key(|p| p.0);\n`;
839
- if (queryParam.explode) {
840
- text += `${indent.get()}for (k, v) in ${queryParam.name}_vec.iter() {\n`;
841
- text += `${indent.push().get()}query_builder.append_pair(*k, v.to_string());\n`;
842
- text += `${indent.pop().get()}}\n`;
843
- }
844
- else {
845
- text += `${indent.get()}query_builder.set_pair("${queryParam.key}", ${queryParam.name}_vec.iter().map(|(k, v)| format!("{k},{v}")).collect::<Vec<String>>().join(","));\n`;
846
- }
847
- text += `${indent.pop().get()}}\n`;
848
- return text;
951
+ const queryParamRef = qualifiedParamName(queryParam);
952
+ return (queryParam.explode)
953
+ ? `${indent.get()}for (k, v) in ${queryParamRef}.iter() {\n`
954
+ + `${indent.push().get()}query_builder.append_pair(k.as_str(), v.to_string());\n`
955
+ + `${indent.pop().get()}}\n`
956
+ : `${indent.get()}let mut ${queryParam.name} = ${queryParamRef}.iter().collect::<Vec<_>>();\n`
957
+ + `${indent.get()}${queryParam.name}.sort_by_key(|p| p.0);\n`
958
+ + `${indent.get()}query_builder.set_pair("${queryParam.key}", ${queryParam.name}.iter().map(|(k, v)| format!("{k},{v}")).collect::<Vec<String>>().join(","));\n`;
849
959
  });
850
960
  }
851
961
  else {
@@ -903,7 +1013,8 @@ function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestV
903
1013
  }
904
1014
  body += getParamValueHelper(indent, headerParam, () => {
905
1015
  if (headerParam.kind === 'headerHashMap') {
906
- let setter = `for (k, v) in ${headerParam.name} {\n`;
1016
+ const headerParamRef = qualifiedParamName(headerParam);
1017
+ let setter = `for (k, v) in ${headerParamRef} {\n`;
907
1018
  setter += `${indent.push().get()}${requestVarName}.insert_header(format!("${headerParam.header}-{k}"), v);\n`;
908
1019
  setter += `${indent.pop().get()}}\n`;
909
1020
  return setter;
@@ -961,15 +1072,15 @@ function constructRequest(indent, use, method, paramGroups, inClosure, urlVarNam
961
1072
  const requestContentType = paramGroups.partialBody[0].type;
962
1073
  use.addForType(requestContentType);
963
1074
  if (inClosure) {
964
- body += `${indent.get()}let body: Result<${helpers.getTypeDeclaration(requestContentType)}> = ${requestContentType.content.type.name} {\n`;
1075
+ body += `${indent.get()}let body: Result<${helpers.getTypeDeclaration(requestContentType)}> = ${requestContentType.content.name} {\n`;
965
1076
  }
966
1077
  else {
967
- body += `${indent.get()}let body: ${helpers.getTypeDeclaration(requestContentType)} = ${requestContentType.content.type.name} {\n`;
1078
+ body += `${indent.get()}let body: ${helpers.getTypeDeclaration(requestContentType)} = ${requestContentType.content.name} {\n`;
968
1079
  }
969
1080
  indent.push();
970
1081
  for (const partialBodyParam of paramGroups.partialBody) {
971
- if (partialBodyParam.type.content.type !== requestContentType.content.type) {
972
- throw new CodegenError('InternalError', `spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.type.name}, expected model type ${requestContentType.content.type.name}`);
1082
+ if (partialBodyParam.type.content !== requestContentType.content) {
1083
+ throw new CodegenError('InternalError', `spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.name}, expected model type ${requestContentType.content.name}`);
973
1084
  }
974
1085
  if (partialBodyParam.optional) {
975
1086
  body += `${indent.get()}${partialBodyParam.name}: options.${partialBodyParam.name}${inClosure ? '.clone()' : ''},\n`;
@@ -979,7 +1090,7 @@ function constructRequest(indent, use, method, paramGroups, inClosure, urlVarNam
979
1090
  if (inClosure) {
980
1091
  initializer = initializer + '.clone()';
981
1092
  }
982
- if (requestContentType.content.type.visibility === 'pub') {
1093
+ if (requestContentType.content.visibility === 'pub') {
983
1094
  // spread param maps to a non-internal model, so it must be wrapped in Some()
984
1095
  initializer = `Some(${initializer})`;
985
1096
  }
@@ -1062,8 +1173,9 @@ function emitEmptyPathParamCheck(indent, param) {
1062
1173
  // no length to check so bail
1063
1174
  return '';
1064
1175
  }
1176
+ const paramRef = qualifiedParamName(param);
1065
1177
  return helpers.buildIfBlock(indent, {
1066
- condition: `${param.name}${toString}.is_empty()`,
1178
+ condition: `${paramRef}${toString}.is_empty()`,
1067
1179
  body: (indent) => `${indent.get()}return Err(azure_core::Error::with_message(azure_core::error::ErrorKind::Other, "parameter ${param.name} cannot be empty"));\n`,
1068
1180
  });
1069
1181
  }
@@ -1647,6 +1759,9 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow, overrid
1647
1759
  else if (overrideParamName) {
1648
1760
  paramName = overrideParamName;
1649
1761
  }
1762
+ else if (param.group) {
1763
+ paramName = qualifiedParamName(param);
1764
+ }
1650
1765
  const encodeBytes = function (type, param) {
1651
1766
  const encoding = helpers.getBytesEncodingMethod(type.encoding, 'encode', use);
1652
1767
  if (param) {
@@ -1675,6 +1790,7 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow, overrid
1675
1790
  // contains the result from calculating if the
1676
1791
  // param requires borrowing.
1677
1792
  let mustBorrow = !helpers.isQueryParameter(param);
1793
+ const isGrouped = !!param.group;
1678
1794
  const paramType = helpers.unwrapType(param.type);
1679
1795
  // we want multi to hit the else case so the necessary conversions etc can happen
1680
1796
  if ((param.kind === 'headerCollection' || param.kind === 'queryCollection') && param.format !== 'multi') {
@@ -1701,8 +1817,8 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow, overrid
1701
1817
  switch (paramType.kind) {
1702
1818
  case 'String':
1703
1819
  paramValue = paramName;
1704
- // if the param is on the client, then we must borrow
1705
- mustBorrow = param.location === 'client' && fromSelf;
1820
+ // if the param is on the client or in a group struct, then we must borrow
1821
+ mustBorrow = isGrouped || (param.location === 'client' && fromSelf);
1706
1822
  break;
1707
1823
  case 'str':
1708
1824
  paramValue = paramName;
@@ -1746,8 +1862,8 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow, overrid
1746
1862
  case 'headerHashMap':
1747
1863
  case 'headerScalar':
1748
1864
  // for non-copyable params (e.g. String), we need to borrow them if they're on the
1749
- // client or we're in a closure and the param is required (header params are always owned)
1750
- mustBorrow = nonCopyableType(param.type) && (param.location === 'client' || (!fromSelf && !param.optional));
1865
+ // client, in a group struct, or we're in a closure and the param is required (header params are always owned)
1866
+ mustBorrow = nonCopyableType(param.type) && (isGrouped || param.location === 'client' || (!fromSelf && !param.optional));
1751
1867
  break;
1752
1868
  }
1753
1869
  return `${mustBorrow && !neverBorrow ? '&' : ''}${paramValue}`;
@@ -1823,4 +1939,8 @@ function isEnumString(type) {
1823
1939
  const unwrapped = helpers.unwrapType(type);
1824
1940
  return unwrapped.kind === 'enum' && unwrapped.type === 'String';
1825
1941
  }
1942
+ /** returns the qualified name for a param, prefixing with the group name when the param belongs to a parameter group */
1943
+ function qualifiedParamName(param) {
1944
+ return param.group ? `${param.group.name}.${param.name}` : param.name;
1945
+ }
1826
1946
  //# sourceMappingURL=clients.js.map