@azure-tools/typespec-rust 0.33.0 → 0.35.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 (67) hide show
  1. package/dist/src/codegen/clients.d.ts.map +1 -1
  2. package/dist/src/codegen/clients.js +120 -93
  3. package/dist/src/codegen/clients.js.map +1 -1
  4. package/dist/src/codegen/codeGenerator.js +16 -16
  5. package/dist/src/codegen/codeGenerator.js.map +1 -1
  6. package/dist/src/codegen/context.js +1 -1
  7. package/dist/src/codegen/context.js.map +1 -1
  8. package/dist/src/codegen/enums.d.ts +5 -5
  9. package/dist/src/codegen/enums.d.ts.map +1 -1
  10. package/dist/src/codegen/enums.js +10 -6
  11. package/dist/src/codegen/enums.js.map +1 -1
  12. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  13. package/dist/src/codegen/headerTraits.js +5 -3
  14. package/dist/src/codegen/headerTraits.js.map +1 -1
  15. package/dist/src/codegen/helpers.d.ts +23 -0
  16. package/dist/src/codegen/helpers.d.ts.map +1 -1
  17. package/dist/src/codegen/helpers.js +38 -18
  18. package/dist/src/codegen/helpers.js.map +1 -1
  19. package/dist/src/codegen/mod.d.ts.map +1 -1
  20. package/dist/src/codegen/mod.js +4 -2
  21. package/dist/src/codegen/mod.js.map +1 -1
  22. package/dist/src/codegen/models.d.ts +4 -6
  23. package/dist/src/codegen/models.d.ts.map +1 -1
  24. package/dist/src/codegen/models.js +170 -70
  25. package/dist/src/codegen/models.js.map +1 -1
  26. package/dist/src/codegen/unions.d.ts +8 -6
  27. package/dist/src/codegen/unions.d.ts.map +1 -1
  28. package/dist/src/codegen/unions.js +142 -9
  29. package/dist/src/codegen/unions.js.map +1 -1
  30. package/dist/src/codegen/use.d.ts +1 -19
  31. package/dist/src/codegen/use.d.ts.map +1 -1
  32. package/dist/src/codegen/use.js +7 -41
  33. package/dist/src/codegen/use.js.map +1 -1
  34. package/dist/src/codemodel/client.d.ts +5 -3
  35. package/dist/src/codemodel/client.d.ts.map +1 -1
  36. package/dist/src/codemodel/client.js +1 -1
  37. package/dist/src/codemodel/client.js.map +1 -1
  38. package/dist/src/codemodel/crate.d.ts +1 -1
  39. package/dist/src/codemodel/crate.d.ts.map +1 -1
  40. package/dist/src/codemodel/customizations.d.ts +12 -0
  41. package/dist/src/codemodel/customizations.d.ts.map +1 -0
  42. package/dist/src/codemodel/customizations.js +13 -0
  43. package/dist/src/codemodel/customizations.js.map +1 -0
  44. package/dist/src/codemodel/index.d.ts +1 -0
  45. package/dist/src/codemodel/index.d.ts.map +1 -1
  46. package/dist/src/codemodel/index.js +1 -0
  47. package/dist/src/codemodel/index.js.map +1 -1
  48. package/dist/src/codemodel/types.d.ts +38 -4
  49. package/dist/src/codemodel/types.d.ts.map +1 -1
  50. package/dist/src/codemodel/types.js +24 -0
  51. package/dist/src/codemodel/types.js.map +1 -1
  52. package/dist/src/tcgcadapter/adapter.d.ts +7 -2
  53. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  54. package/dist/src/tcgcadapter/adapter.js +266 -173
  55. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  56. package/dist/src/tcgcadapter/naming.d.ts.map +1 -1
  57. package/dist/src/tcgcadapter/naming.js +2 -2
  58. package/dist/src/tcgcadapter/naming.js.map +1 -1
  59. package/dist/src/utils/utils.d.ts +99 -0
  60. package/dist/src/utils/utils.d.ts.map +1 -0
  61. package/dist/src/utils/utils.js +288 -0
  62. package/dist/src/utils/utils.js.map +1 -0
  63. package/package.json +16 -19
  64. package/dist/src/shared/shared.d.ts +0 -29
  65. package/dist/src/shared/shared.d.ts.map +0 -1
  66. package/dist/src/shared/shared.js +0 -109
  67. package/dist/src/shared/shared.js.map +0 -1
@@ -3,12 +3,10 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  // cspell: ignore addl responseheader subclients lropaging
6
- import * as codegen from '@azure-tools/codegen';
7
- import { values } from '@azure-tools/linq';
8
6
  import * as tsp from '@typespec/compiler';
9
7
  import * as helpers from './helpers.js';
10
8
  import * as naming from './naming.js';
11
- import * as shared from '../shared/shared.js';
9
+ import * as utils from '../utils/utils.js';
12
10
  import * as tcgc from '@azure-tools/typespec-client-generator-core';
13
11
  import * as rust from '../codemodel/index.js';
14
12
  import { FinalStateValue } from "@azure-tools/typespec-azure-core";
@@ -75,7 +73,7 @@ export class Adapter {
75
73
  this.adaptTypes();
76
74
  this.adaptClients();
77
75
  // marker models don't require serde so exclude them from the check
78
- if (this.crate.enums.length > 0 || values(this.crate.models).where(e => e.kind === 'model').any()) {
76
+ if (this.crate.enums.length > 0 || this.crate.models.some(e => e.kind === 'model')) {
79
77
  this.crate.addDependency(new rust.CrateDependency('serde'));
80
78
  }
81
79
  if (this.crate.clients.length > 0 || this.crate.enums.length > 0 || this.crate.models.length > 0 || this.crate.unions.length > 0) {
@@ -137,6 +135,14 @@ export class Adapter {
137
135
  processedTypes.add(model.name);
138
136
  }
139
137
  // END workaround
138
+ if (model.discriminatedSubtypes) {
139
+ const rustUnion = this.getDiscriminatedUnion(model);
140
+ this.crate.unions.push(rustUnion);
141
+ // we don't want to add the base type to the array
142
+ // of models to emit as we hijack its name to be used
143
+ // for the associated tagged enum type.
144
+ continue;
145
+ }
140
146
  if (model.external) {
141
147
  this.getExternalType(model.external);
142
148
  }
@@ -154,7 +160,7 @@ export class Adapter {
154
160
  */
155
161
  getEnum(sdkEnum) {
156
162
  var _a, _b, _c;
157
- const enumName = codegen.deconstruct(sdkEnum.name).map((each) => codegen.capitalize(each)).join('');
163
+ const enumName = utils.deconstruct(sdkEnum.name).map((each) => utils.capitalize(each)).join('');
158
164
  let rustEnum = this.types.get(enumName);
159
165
  if (rustEnum) {
160
166
  return rustEnum;
@@ -277,7 +283,7 @@ export class Adapter {
277
283
  }
278
284
  // remove any non-word characters from the name.
279
285
  // the most common case is something like Foo.Bar.Baz
280
- modelName = codegen.capitalize(modelName).replace(/\W/g, '');
286
+ modelName = utils.capitalize(modelName).replace(/\W/g, '');
281
287
  let rustModel = this.types.get(modelName);
282
288
  if (rustModel) {
283
289
  return rustModel;
@@ -287,33 +293,43 @@ export class Adapter {
287
293
  if (!stack) {
288
294
  stack = new Array();
289
295
  }
290
- stack.push(modelName);
291
296
  let modelFlags = rust.ModelFlags.Unspecified;
292
297
  if ((model.usage & tcgc.UsageFlags.Input) === tcgc.UsageFlags.Input) {
293
298
  modelFlags |= rust.ModelFlags.Input;
294
299
  }
295
- if ((model.usage & tcgc.UsageFlags.Output) === tcgc.UsageFlags.Output) {
300
+ // include error and LRO polling types as output types
301
+ if ((model.usage & tcgc.UsageFlags.Output) === tcgc.UsageFlags.Output ||
302
+ (model.usage & tcgc.UsageFlags.Exception) === tcgc.UsageFlags.Exception ||
303
+ (model.usage & tcgc.UsageFlags.LroPolling) === tcgc.UsageFlags.LroPolling) {
296
304
  modelFlags |= rust.ModelFlags.Output;
297
305
  }
298
306
  rustModel = new rust.Model(modelName, model.access === 'internal' ? 'pubCrate' : 'pub', modelFlags);
299
307
  rustModel.docs = this.adaptDocs(model.summary, model.doc);
300
308
  rustModel.xmlName = getXMLName(model.decorators);
301
309
  this.types.set(modelName, rustModel);
310
+ stack.push(rustModel);
302
311
  // aggregate the properties from the provided type and its parent types
303
312
  const allProps = new Array();
304
313
  for (const prop of model.properties) {
314
+ if (prop.discriminator && !model.discriminatedSubtypes) {
315
+ rustModel.flags |= rust.ModelFlags.PolymorphicSubtype;
316
+ }
305
317
  allProps.push(prop);
306
318
  }
307
319
  let addlProps = model.additionalProperties;
308
320
  let parent = model.baseModel;
309
321
  while (parent) {
310
322
  for (const parentProp of parent.properties) {
311
- const exists = values(allProps).where(p => { return p.name === parentProp.name; }).first();
312
- if (exists) {
323
+ if (allProps.find(p => p.name === parentProp.name)) {
313
324
  // don't add the duplicate. the TS compiler has better enforcement than OpenAPI
314
325
  // to ensure that duplicate fields with different types aren't added.
315
326
  continue;
316
327
  }
328
+ else if (parentProp.discriminator) {
329
+ // we don't propagate the discriminator to the child
330
+ // types as it's not useful (or necessary)
331
+ continue;
332
+ }
317
333
  allProps.push(parentProp);
318
334
  }
319
335
  // propagate parent's additional properties if we don't yet have any.
@@ -342,13 +358,13 @@ export class Adapter {
342
358
  throw new AdapterError('UnsupportedTsp', `model property kind ${(_c = property.__raw) === null || _c === void 0 ? void 0 : _c.kind} NYI`, (_d = property.__raw) === null || _d === void 0 ? void 0 : _d.node);
343
359
  }
344
360
  }
345
- const structField = this.getModelField(property, rustModel.visibility, stack);
361
+ const structField = this.getModelField(model.usage, property, rustModel.visibility, stack);
346
362
  rustModel.fields.push(structField);
347
363
  }
348
364
  if (addlProps) {
349
365
  const addlPropsType = this.getHashMap(this.typeToWireType(this.getType(addlProps)));
350
366
  const addlPropsField = new rust.ModelAdditionalProperties('additional_properties', 'pub', this.getOptionType(addlPropsType));
351
- addlPropsField.docs.summary = 'contains unnamed additional properties';
367
+ addlPropsField.docs.summary = 'Contains unnamed additional properties.';
352
368
  rustModel.fields.push(addlPropsField);
353
369
  }
354
370
  stack.pop();
@@ -360,55 +376,100 @@ export class Adapter {
360
376
  * @param union the tcgc union to convert
361
377
  * @returns a Rust union
362
378
  */
363
- getDiscriminatedUnion(union) {
364
- var _a, _b, _c, _d;
365
- if (union.name.length === 0) {
366
- throw new AdapterError('InternalError', 'unnamed union', (_a = union.__raw) === null || _a === void 0 ? void 0 : _a.node);
367
- }
368
- const unionName = codegen.capitalize(union.name);
369
- let rustUnion = this.types.get(unionName);
379
+ getDiscriminatedUnion(src) {
380
+ var _a, _b, _c, _d, _e, _f, _g;
381
+ if (src.name.length === 0) {
382
+ throw new AdapterError('InternalError', 'unnamed union', (_a = src.__raw) === null || _a === void 0 ? void 0 : _a.node);
383
+ }
384
+ const unionName = utils.deconstruct(src.name).map((each) => utils.capitalize(each)).join('');
385
+ const keyName = `discriminated-union-${unionName}`;
386
+ let rustUnion = this.types.get(keyName);
370
387
  if (rustUnion) {
371
388
  return rustUnion;
372
389
  }
373
- if (!union.discriminatedOptions) {
374
- // we should have verified this earlier.
375
- // having this check means the compiler won't bark
376
- // at us when accessing union.discriminatedOptions
377
- throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-discriminated union', (_b = union.__raw) === null || _b === void 0 ? void 0 : _b.node);
378
- }
379
- rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(union.access), union.discriminatedOptions.discriminatorPropertyName);
380
- rustUnion.envelopeName = union.discriminatedOptions.envelopePropertyName;
381
- rustUnion.docs = this.adaptDocs(union.summary, union.doc);
382
- this.types.set(unionName, rustUnion);
383
- // TODO: remove when https://github.com/microsoft/typespec/issues/8455 is complete
384
- const discriminatorValues = new Map();
385
- const rawUnion = union.__raw;
386
- if (rawUnion) {
387
- for (const [variantKey, variant] of rawUnion.variants) {
388
- const discriminatorValue = typeof variantKey === 'string' ? variantKey : variantKey.toString();
389
- if (variant.type.kind === 'Model') {
390
- discriminatorValues.set(variant.type.name, discriminatorValue);
390
+ switch (src.kind) {
391
+ case 'model': {
392
+ if (!src.discriminatedSubtypes) {
393
+ // we should have verified this earlier.
394
+ // having this check means the compiler won't bark
395
+ // at us when accessing src.discriminatedSubtypes
396
+ throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-polymorphic model', (_b = src.__raw) === null || _b === void 0 ? void 0 : _b.node);
397
+ }
398
+ // find the discriminator field
399
+ let discriminatorProperty;
400
+ for (const prop of src.properties) {
401
+ if (prop.kind === 'property' && prop.discriminator) {
402
+ discriminatorProperty = prop;
403
+ break;
404
+ }
405
+ }
406
+ if (!discriminatorProperty) {
407
+ throw new AdapterError('InternalError', `failed to find discriminator field for type ${src.name}`, (_c = src.__raw) === null || _c === void 0 ? void 0 : _c.node);
408
+ }
409
+ rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), discriminatorProperty.name);
410
+ if (discriminatorProperty.type.kind === 'enum' && discriminatorProperty.type.isFixed) {
411
+ // when the DU is a fixed enum, it means we don't fall back to the
412
+ // base type when the discriminator value is unknown or missing.
413
+ rustUnion.unionKind = new rust.DiscriminatedUnionSealed();
391
414
  }
392
415
  else {
393
- throw new AdapterError('InternalError', `unexpected kind ${variant.type.kind} for discriminated union member`, rawUnion);
416
+ rustUnion.unionKind = new rust.DiscriminatedUnionBase(this.getModel(src));
394
417
  }
418
+ for (const subType of Object.values(src.discriminatedSubtypes)) {
419
+ if (!subType.discriminatorValue) {
420
+ throw new AdapterError('InternalError', `model ${subType.name} has no discriminator value`, (_d = subType.__raw) === null || _d === void 0 ? void 0 : _d.node);
421
+ }
422
+ const unionMemberType = this.getModel(subType);
423
+ const rustUnionMember = new rust.DiscriminatedUnionMember(unionMemberType, subType.discriminatorValue);
424
+ rustUnion.members.push(rustUnionMember);
425
+ }
426
+ break;
395
427
  }
396
- }
397
- // END WORKAROUND
398
- for (const unionMember of union.variantTypes) {
399
- if (unionMember.kind !== 'model') {
400
- throw new AdapterError('UnsupportedTsp', `non-model union member kind ${unionMember.kind}`, (_c = unionMember.__raw) === null || _c === void 0 ? void 0 : _c.node);
401
- }
402
- // TODO: use unionMember.discriminatorValue
403
- const discriminatorValue = discriminatorValues.get(unionMember.name);
404
- if (!discriminatorValue) {
405
- throw new AdapterError('InternalError', `didn't find discriminant value for type ${unionMember.name}`, (_d = unionMember.__raw) === null || _d === void 0 ? void 0 : _d.node);
428
+ case 'union': {
429
+ if (!src.discriminatedOptions) {
430
+ // we should have verified this earlier.
431
+ // having this check means the compiler won't bark
432
+ // at us when accessing src.discriminatedOptions
433
+ throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-discriminated union', (_e = src.__raw) === null || _e === void 0 ? void 0 : _e.node);
434
+ }
435
+ rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), src.discriminatedOptions.discriminatorPropertyName);
436
+ if (src.discriminatedOptions.envelopePropertyName) {
437
+ rustUnion.unionKind = new rust.DiscriminatedUnionEnvelope(src.discriminatedOptions.envelopePropertyName);
438
+ }
439
+ // TODO: remove when https://github.com/microsoft/typespec/issues/8455 is complete
440
+ const discriminatorValues = new Map();
441
+ const rawUnion = src.__raw;
442
+ if (rawUnion) {
443
+ for (const [variantKey, variant] of rawUnion.variants) {
444
+ const discriminatorValue = typeof variantKey === 'string' ? variantKey : variantKey.toString();
445
+ if (variant.type.kind === 'Model') {
446
+ discriminatorValues.set(variant.type.name, discriminatorValue);
447
+ }
448
+ else {
449
+ throw new AdapterError('InternalError', `unexpected kind ${variant.type.kind} for discriminated union member`, rawUnion);
450
+ }
451
+ }
452
+ }
453
+ // END WORKAROUND
454
+ for (const unionMember of src.variantTypes) {
455
+ if (unionMember.kind !== 'model') {
456
+ throw new AdapterError('UnsupportedTsp', `non-model union member kind ${unionMember.kind}`, (_f = unionMember.__raw) === null || _f === void 0 ? void 0 : _f.node);
457
+ }
458
+ // TODO: use unionMember.discriminatorValue
459
+ const discriminatorValue = discriminatorValues.get(unionMember.name);
460
+ if (!discriminatorValue) {
461
+ throw new AdapterError('InternalError', `didn't find discriminant value for type ${unionMember.name}`, (_g = unionMember.__raw) === null || _g === void 0 ? void 0 : _g.node);
462
+ }
463
+ const unionMemberType = this.getModel(unionMember);
464
+ const rustUnionMember = new rust.DiscriminatedUnionMember(unionMemberType, discriminatorValue);
465
+ rustUnionMember.docs = this.adaptDocs(unionMember.summary, unionMember.doc);
466
+ rustUnion.members.push(rustUnionMember);
467
+ }
468
+ break;
406
469
  }
407
- const unionMemberType = this.getModel(unionMember);
408
- const rustUnionMember = new rust.DiscriminatedUnionMember(unionMemberType, discriminatorValue);
409
- rustUnionMember.docs = this.adaptDocs(unionMember.summary, unionMember.doc);
410
- rustUnion.members.push(rustUnionMember);
411
470
  }
471
+ rustUnion.docs = this.adaptDocs(src.summary, src.doc);
472
+ this.types.set(keyName, rustUnion);
412
473
  return rustUnion;
413
474
  }
414
475
  getSerializedPropertyName(property) {
@@ -418,30 +479,58 @@ export class Adapter {
418
479
  /**
419
480
  * converts a tcgc model property to a model field
420
481
  *
482
+ * @param modelFlags the flags for the model to which the field belongs
421
483
  * @param property the tcgc model property to convert
422
484
  * @param modelVisibility the visibility of the model that contains the property
423
- * @param stack is a stack of model type names used to detect recursive type definitions
485
+ * @param stack is a stack of types used to detect recursive type definitions
424
486
  * @returns a Rust model field
425
487
  */
426
- getModelField(property, modelVisibility, stack) {
427
- var _a;
488
+ getModelField(modelFlags, property, modelVisibility, stack) {
489
+ var _a, _b, _c, _d, _e;
490
+ const fieldNeedsBoxing = function (fieldType) {
491
+ if (fieldType.kind === 'model' && stack.includes(fieldType)) {
492
+ // if the field's type is a model and it's in the type stack then
493
+ // box it. this is to avoid infinitely recursive type definitions.
494
+ return true;
495
+ }
496
+ else if (fieldType.kind === 'discriminatedUnion') {
497
+ // if the field is a discriminated union whose type
498
+ // is part of the same union then box it.
499
+ for (const member of fieldType.members) {
500
+ if (stack.includes(member.type)) {
501
+ return true;
502
+ }
503
+ }
504
+ }
505
+ return false;
506
+ };
428
507
  let fieldType = this.getType(property.type, stack);
429
- // if the field's type is a model and it's in the type stack then
430
- // box it. this is to avoid infinitely recursive type definitions.
431
- if (fieldType.kind === 'model' && stack.includes(fieldType.name)) {
508
+ if (fieldNeedsBoxing(fieldType)) {
432
509
  fieldType = this.getBoxType(fieldType);
433
510
  }
434
- // for public models each field is always an Option<T>.
435
- if (modelVisibility === 'pub' || property.optional) {
511
+ // for non-spread models each field is always an Option<T>.
512
+ // NOTE: models can be used for both spread and I/O, so when
513
+ // restricting for spread it must be ONLY used for spread.
514
+ const notSpreadOnly = (modelFlags & tcgc.UsageFlags.Spread) === 0 || (modelFlags & tcgc.UsageFlags.Input) || (modelFlags & tcgc.UsageFlags.Output);
515
+ if (notSpreadOnly || property.optional) {
436
516
  fieldType = this.getOptionType(fieldType.kind === 'box' ? fieldType : this.typeToWireType(fieldType));
437
517
  }
438
518
  const serializedName = (_a = this.getSerializedPropertyName(property)) !== null && _a !== void 0 ? _a : property.name;
439
- const modelField = new rust.ModelField(naming.getEscapedReservedName(snakeCaseName(property.name), 'prop'), serializedName, modelVisibility, fieldType, property.optional);
519
+ const modelField = new rust.ModelField(naming.getEscapedReservedName(utils.snakeCaseName(property.name), 'prop'), serializedName, modelVisibility, fieldType, property.optional);
440
520
  modelField.docs = this.adaptDocs(property.summary, property.doc);
441
521
  // if this is a literal, add a doc comment explaining its behavior
442
- const unwrappedType = shared.unwrapOption(fieldType);
443
- if (unwrappedType.kind === 'literal') {
444
- const literalDoc = `${modelField.optional ? 'When Some, field' : 'Field'} has constant value ${unwrappedType.value}. Any specified value will be ignored.`;
522
+ const unwrappedType = utils.unwrapOption(fieldType);
523
+ if (unwrappedType.kind === 'enumValue' || unwrappedType.kind === 'literal') {
524
+ let constValue;
525
+ switch (unwrappedType.kind) {
526
+ case 'enumValue':
527
+ constValue = `${unwrappedType.type.name}::${unwrappedType.name}`;
528
+ break;
529
+ case 'literal':
530
+ constValue = unwrappedType.value;
531
+ break;
532
+ }
533
+ const literalDoc = `${modelField.optional ? 'When Some, field' : 'Field'} has constant value ${constValue}. Any specified value will be ignored.`;
445
534
  if (!modelField.docs.description) {
446
535
  modelField.docs.description = '';
447
536
  }
@@ -463,17 +552,51 @@ export class Adapter {
463
552
  if (property.decorators.find((decorator) => decorator.name === 'Azure.ClientGenerator.Core.@deserializeEmptyStringAsNull') !== undefined) {
464
553
  modelField.flags |= rust.ModelFieldFlags.DeserializeEmptyStringAsNone;
465
554
  }
555
+ else if (property.kind === 'property' && property.discriminator) {
556
+ modelField.flags |= rust.ModelFieldFlags.Discriminator;
557
+ }
558
+ // check for any client options on the field
559
+ const clientOptions = property.decorators.filter((decorator) => decorator.name === 'Azure.ClientGenerator.Core.@clientOption');
560
+ for (const clientOption of clientOptions) {
561
+ const optionName = clientOption.arguments['name'];
562
+ const optionValue = clientOption.arguments['value'];
563
+ switch (optionName) {
564
+ case 'deserialize_with':
565
+ if (modelField.customizations.find((each) => each.kind === 'deserializeWith')) {
566
+ // ignore any duplicates and warn about it
567
+ this.ctx.program.reportDiagnostic({
568
+ code: 'DuplicateClientOption',
569
+ severity: 'warning',
570
+ message: `duplicate client option ${optionName} on model field ${property.name} will be ignored`,
571
+ target: (_c = (_b = property.__raw) === null || _b === void 0 ? void 0 : _b.node) !== null && _c !== void 0 ? _c : tsp.NoTarget,
572
+ });
573
+ continue;
574
+ }
575
+ modelField.customizations.push(new rust.DeserializeWith(optionValue));
576
+ break;
577
+ default:
578
+ this.ctx.program.reportDiagnostic({
579
+ code: 'InvalidClientOption',
580
+ severity: 'warning',
581
+ message: `invalid client option ${optionName} on model field ${property.name}`,
582
+ target: (_e = (_d = property.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : tsp.NoTarget,
583
+ });
584
+ }
585
+ }
466
586
  return modelField;
467
587
  }
468
588
  /**
469
589
  * converts a tcgc type to a Rust type
470
590
  *
471
591
  * @param type the tcgc type to convert
472
- * @param stack is a stack of model type names used to detect recursive type definitions
592
+ * @param stack is a stack of types used to detect recursive type definitions
473
593
  * @returns the adapted Rust type
474
594
  */
475
595
  getType(type, stack) {
476
596
  var _a, _b, _c;
597
+ if (type.external) {
598
+ return this.getExternalType(type.external);
599
+ }
477
600
  const getDateTimeEncoding = (encoding) => {
478
601
  var _a;
479
602
  switch (encoding) {
@@ -549,24 +672,19 @@ export class Adapter {
549
672
  if (type.external) {
550
673
  return this.getExternalType(type.external);
551
674
  }
675
+ else if (type.discriminatedSubtypes) {
676
+ return this.getDiscriminatedUnion(type);
677
+ }
552
678
  return this.getModel(type, stack);
553
679
  case 'endpoint':
554
680
  case 'plainDate':
555
681
  case 'plainTime':
556
682
  case 'string':
557
- case 'url': {
683
+ case 'url':
558
684
  if (type.kind === 'string' && type.crossLanguageDefinitionId === 'Azure.Core.eTag') {
559
- const etagKey = 'Etag';
560
- let etagType = this.types.get(etagKey);
561
- if (etagType) {
562
- return etagType;
563
- }
564
- etagType = new rust.Etag(this.crate);
565
- this.types.set(etagKey, etagType);
566
- return etagType;
685
+ return this.getEtag();
567
686
  }
568
687
  return this.getStringType();
569
- }
570
688
  case 'nullable':
571
689
  if (type.type.kind === 'model' && type.type.isGeneratedName) {
572
690
  // if the nullable type's target type is a synthesized
@@ -595,16 +713,8 @@ export class Adapter {
595
713
  }
596
714
  return safeint;
597
715
  }
598
- case 'unknown': {
599
- const keyName = 'jsonValue';
600
- let anyType = this.types.get(keyName);
601
- if (anyType) {
602
- return anyType;
603
- }
604
- anyType = new rust.JsonValue(this.crate);
605
- this.types.set(keyName, anyType);
606
- return anyType;
607
- }
716
+ case 'unknown':
717
+ return this.getUnknownValue();
608
718
  case 'utcDateTime': {
609
719
  const encoding = getDateTimeEncoding(type.encode);
610
720
  const keyName = `offsetDateTime-${encoding}-utc`;
@@ -647,6 +757,17 @@ export class Adapter {
647
757
  this.types.set(keyName, encodedBytesType);
648
758
  return encodedBytesType;
649
759
  }
760
+ /** returns a Etag type */
761
+ getEtag() {
762
+ const etagKey = 'Etag';
763
+ let etagType = this.types.get(etagKey);
764
+ if (etagType) {
765
+ return etagType;
766
+ }
767
+ etagType = new rust.Etag(this.crate);
768
+ this.types.set(etagKey, etagType);
769
+ return etagType;
770
+ }
650
771
  /** returns a HashMap<String, type> */
651
772
  getHashMap(type) {
652
773
  const keyName = recursiveKeyName('hashmap', type);
@@ -658,6 +779,17 @@ export class Adapter {
658
779
  this.types.set(keyName, hashmapType);
659
780
  return hashmapType;
660
781
  }
782
+ /** returns an azure_core::Value */
783
+ getUnknownValue() {
784
+ const keyName = 'jsonValue';
785
+ let anyType = this.types.get(keyName);
786
+ if (anyType) {
787
+ return anyType;
788
+ }
789
+ anyType = new rust.JsonValue(this.crate);
790
+ this.types.set(keyName, anyType);
791
+ return anyType;
792
+ }
661
793
  /** returns an Option<T> where T is specified in type */
662
794
  getOptionType(type) {
663
795
  const typeKey = recursiveKeyName('option', type);
@@ -823,10 +955,7 @@ export class Adapter {
823
955
  /** converts all tcgc clients and their methods into Rust clients/methods */
824
956
  adaptClients() {
825
957
  for (const client of this.ctx.sdkPackage.clients) {
826
- // start with instantiable clients and recursively work down
827
- if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
828
- this.recursiveAdaptClient(client);
829
- }
958
+ this.recursiveAdaptClient(client);
830
959
  }
831
960
  }
832
961
  /**
@@ -853,7 +982,7 @@ export class Adapter {
853
982
  * @returns a Rust client
854
983
  */
855
984
  recursiveAdaptClient(client, parent) {
856
- var _a, _b, _c, _d;
985
+ var _a, _b, _c, _d, _e;
857
986
  let clientName = client.name;
858
987
  // NOTE: if the client has the @clientName decorator applied then use that verbatim
859
988
  if (parent && !hasClientNameDecorator(client.decorators)) {
@@ -880,16 +1009,22 @@ export class Adapter {
880
1009
  rustClient.docs = this.adaptDocs(client.summary, client.doc);
881
1010
  rustClient.parent = parent;
882
1011
  rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'Pipeline', 'azure_core::http')));
883
- // anything other than public means non-instantiable client
884
- if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
1012
+ // InitializedByFlags.CustomizeCode means the client is instantiable
1013
+ // but the constructor is to be omitted (i.e. hand-written).
1014
+ if (client.clientInitialization.initializedBy === tcgc.InitializedByFlags.CustomizeCode || client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
885
1015
  const clientOptionsStruct = new rust.Struct(`${rustClient.name}Options`, 'pub');
886
1016
  const clientOptionsField = new rust.StructField('client_options', 'pub', new rust.ExternalType(this.crate, 'ClientOptions', 'azure_core::http'));
887
1017
  clientOptionsField.docs.summary = 'Allows customization of the client.';
888
1018
  clientOptionsField.defaultValue = 'ClientOptions::default()';
889
1019
  clientOptionsStruct.fields.push(clientOptionsField);
890
1020
  rustClient.constructable = new rust.ClientConstruction(new rust.ClientOptions(clientOptionsStruct));
891
- rustClient.constructable.suppressed = this.options['omit-constructors'];
892
1021
  clientOptionsStruct.docs.summary = `Options used when creating a ${this.asDocLink(rustClient.name, rustClient.name)}`;
1022
+ if (this.options['omit-constructors']) {
1023
+ rustClient.constructable.suppressed = 'yes';
1024
+ }
1025
+ else if (client.clientInitialization.initializedBy === tcgc.InitializedByFlags.CustomizeCode) {
1026
+ rustClient.constructable.suppressed = 'ctor';
1027
+ }
893
1028
  // NOTE: per tcgc convention, if there is no param of kind credential
894
1029
  // it means that the client doesn't require any kind of authentication.
895
1030
  // HOWEVER, if there *is* a credential param, then the client *does not*
@@ -977,7 +1112,7 @@ export class Adapter {
977
1112
  // note that the types of the param and the field are different.
978
1113
  // we default to "endpoint" and will use the defined name IFF
979
1114
  // it has the @clientName decorator applied
980
- const endpointName = hasClientNameDecorator(templateArg.decorators) ? snakeCaseName(templateArg.name) : 'endpoint';
1115
+ const endpointName = hasClientNameDecorator(templateArg.decorators) ? utils.snakeCaseName(templateArg.name) : 'endpoint';
981
1116
  const adaptedParam = new rust.ClientEndpointParameter(endpointName);
982
1117
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
983
1118
  ctorParams.push(adaptedParam);
@@ -1007,6 +1142,11 @@ export class Adapter {
1007
1142
  break;
1008
1143
  }
1009
1144
  case 'method': {
1145
+ // https://github.com/Azure/typespec-rust/issues/849
1146
+ // Azure doesn't use optional (with no explicit default value) API versions anyway.
1147
+ if (param.isApiVersionParam && param.optional && !param.clientDefaultValue) {
1148
+ param.optional = false;
1149
+ }
1010
1150
  const clientParam = this.adaptClientParameter(param, rustClient.constructable);
1011
1151
  rustClient.fields.push(new rust.StructField(clientParam.name, 'pubCrate', clientParam.type));
1012
1152
  ctorParams.push(clientParam);
@@ -1032,7 +1172,7 @@ export class Adapter {
1032
1172
  // to create a child client that will need to inherit our client params.
1033
1173
  rustClient.endpoint = parent.endpoint;
1034
1174
  for (const prop of client.clientInitialization.parameters) {
1035
- const name = snakeCaseName(prop.name);
1175
+ const name = utils.snakeCaseName(prop.name);
1036
1176
  const parentField = parent.fields.find((v) => v.name === name);
1037
1177
  if (parentField) {
1038
1178
  rustClient.fields.push(parentField);
@@ -1050,7 +1190,7 @@ export class Adapter {
1050
1190
  else {
1051
1191
  throw new AdapterError('InternalError', `uninstantiatable client ${client.name} has no parent`);
1052
1192
  }
1053
- for (const child of values(client.children)) {
1193
+ for (const child of (_c = client.children) !== null && _c !== void 0 ? _c : []) {
1054
1194
  const subClient = this.recursiveAdaptClient(child, rustClient);
1055
1195
  this.adaptClientAccessor(client, child, rustClient, subClient);
1056
1196
  }
@@ -1062,7 +1202,7 @@ export class Adapter {
1062
1202
  code: 'LroPagingNotSupported',
1063
1203
  severity: 'warning',
1064
1204
  message: `skip emitting Paging LRO ${method.name}`,
1065
- target: (_d = (_c = method.__raw) === null || _c === void 0 ? void 0 : _c.node) !== null && _d !== void 0 ? _d : tsp.NoTarget,
1205
+ target: (_e = (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : tsp.NoTarget,
1066
1206
  });
1067
1207
  continue;
1068
1208
  }
@@ -1106,22 +1246,8 @@ export class Adapter {
1106
1246
  * @returns the Rust client parameter
1107
1247
  */
1108
1248
  adaptClientParameter(param, constructable) {
1109
- let paramType;
1110
- // the second clause is a workaround for https://github.com/Azure/typespec-azure/issues/2745
1111
- if (param.isApiVersionParam || (param.type.kind === 'enum' && (param.type.usage & tcgc.UsageFlags.ApiVersionEnum) === tcgc.UsageFlags.ApiVersionEnum)) {
1112
- if (param.clientDefaultValue) {
1113
- // this is optional so it goes into the client options type as a String
1114
- paramType = this.getStringType();
1115
- }
1116
- else {
1117
- // this is a required param so its type is a &str
1118
- paramType = this.getRefType(this.getStringSlice());
1119
- }
1120
- }
1121
- else {
1122
- paramType = this.getType(param.type);
1123
- }
1124
- const paramName = snakeCaseName(param.name);
1249
+ let paramType = param.isApiVersionParam ? this.getStringType() : this.getType(param.type);
1250
+ const paramName = utils.snakeCaseName(param.name);
1125
1251
  let optional = false;
1126
1252
  // client-side default value makes the param optional
1127
1253
  if (param.optional || param.clientDefaultValue) {
@@ -1157,7 +1283,7 @@ export class Adapter {
1157
1283
  * @param subClient the sub-client type that the method returns
1158
1284
  */
1159
1285
  adaptClientAccessor(parentClient, childClient, rustClient, subClient) {
1160
- const clientAccessor = new rust.ClientAccessor(`get_${snakeCaseName(subClient.name)}`, rustClient, subClient);
1286
+ const clientAccessor = new rust.ClientAccessor(`get_${utils.snakeCaseName(subClient.name)}`, rustClient, subClient);
1161
1287
  clientAccessor.docs.summary = `Returns a new instance of ${subClient.name}.`;
1162
1288
  for (const param of childClient.clientInitialization.parameters) {
1163
1289
  // check if the client's initializer already has this parameter.
@@ -1173,7 +1299,7 @@ export class Adapter {
1173
1299
  if (existsOnParent) {
1174
1300
  continue;
1175
1301
  }
1176
- const adaptedParam = new rust.Parameter(snakeCaseName(param.name), this.getType(param.type));
1302
+ const adaptedParam = new rust.Parameter(utils.snakeCaseName(param.name), this.getType(param.type));
1177
1303
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
1178
1304
  clientAccessor.params.push(adaptedParam);
1179
1305
  }
@@ -1189,14 +1315,14 @@ export class Adapter {
1189
1315
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
1190
1316
  let srcMethodName = method.name;
1191
1317
  if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
1192
- const chunks = codegen.deconstruct(srcMethodName);
1318
+ const chunks = utils.deconstruct(srcMethodName);
1193
1319
  if (chunks[0] === 'get') {
1194
1320
  chunks[0] = 'list';
1195
1321
  }
1196
1322
  else {
1197
1323
  chunks.unshift('list');
1198
1324
  }
1199
- srcMethodName = codegen.camelCase(chunks);
1325
+ srcMethodName = utils.camelCase(chunks);
1200
1326
  this.ctx.program.reportDiagnostic({
1201
1327
  code: 'PagingMethodRename',
1202
1328
  severity: 'warning',
@@ -1205,7 +1331,8 @@ export class Adapter {
1205
1331
  });
1206
1332
  }
1207
1333
  const languageIndependentName = method.crossLanguageDefinitionId;
1208
- const methodName = naming.getEscapedReservedName(snakeCaseName(srcMethodName), 'fn');
1334
+ const methodName = naming.getEscapedReservedName(utils.snakeCaseName(srcMethodName), 'fn');
1335
+ const pub = adaptAccessFlags(method.access);
1209
1336
  if (srcMethodName !== method.name) {
1210
1337
  // if the method was renamed then ensure it doesn't collide
1211
1338
  for (const existingMethod of rustClient.methods) {
@@ -1215,7 +1342,7 @@ export class Adapter {
1215
1342
  }
1216
1343
  }
1217
1344
  const optionsLifetime = new rust.Lifetime('a');
1218
- const methodOptionsStruct = new rust.Struct(`${rustClient.name}${codegen.pascalCase(srcMethodName, false)}Options`, 'pub');
1345
+ const methodOptionsStruct = new rust.Struct(`${rustClient.name}${utils.pascalCase(srcMethodName, false)}Options`, pub);
1219
1346
  methodOptionsStruct.lifetime = optionsLifetime;
1220
1347
  methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
1221
1348
  let clientMethodOptions;
@@ -1230,10 +1357,9 @@ export class Adapter {
1230
1357
  default:
1231
1358
  clientMethodOptions = new rust.ClientMethodOptions(this.crate, optionsLifetime);
1232
1359
  }
1233
- const methodOptionsField = new rust.StructField('method_options', 'pub', clientMethodOptions);
1360
+ const methodOptionsField = new rust.StructField('method_options', pub, clientMethodOptions);
1234
1361
  methodOptionsField.docs.summary = 'Allows customization of the method call.';
1235
1362
  methodOptionsStruct.fields.push(methodOptionsField);
1236
- const pub = adaptAccessFlags(method.access);
1237
1363
  const methodOptions = new rust.MethodOptions(methodOptionsStruct);
1238
1364
  const httpMethod = method.operation.verb;
1239
1365
  let rustMethod;
@@ -1290,12 +1416,11 @@ export class Adapter {
1290
1416
  // most params have a one-to-one mapping. however, for spread params, there will
1291
1417
  // be a many-to-one mapping. i.e. multiple params will map to the same underlying
1292
1418
  // operation param. each param corresponds to a field within the operation param.
1293
- const opParam = values(allOpParams).where((opParam) => {
1294
- return values(opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]))
1295
- .where((methodParam) => {
1419
+ const opParam = allOpParams.find((opParam) => {
1420
+ return opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).some((methodParam) => {
1296
1421
  return methodParam.name === param.name;
1297
- }).any();
1298
- }).first();
1422
+ });
1423
+ });
1299
1424
  if (!opParam) {
1300
1425
  throw new AdapterError('InternalError', `didn't find operation parameter for method ${method.name} parameter ${param.name}`, (_f = param.__raw) === null || _f === void 0 ? void 0 : _f.node);
1301
1426
  }
@@ -1344,7 +1469,7 @@ export class Adapter {
1344
1469
  else {
1345
1470
  fieldType = this.getOptionType(adaptedParam.type);
1346
1471
  }
1347
- const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
1472
+ const optionsField = new rust.StructField(adaptedParam.name, pub, fieldType);
1348
1473
  optionsField.docs = adaptedParam.docs;
1349
1474
  rustMethod.options.type.fields.push(optionsField);
1350
1475
  }
@@ -1495,7 +1620,7 @@ export class Adapter {
1495
1620
  }
1496
1621
  else if (method.kind === 'lro') {
1497
1622
  const format = responseFormat === 'NoFormat' ? 'JsonFormat' : responseFormat;
1498
- const statusType = this.typeToWireType(this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${codegen.pascalCase(rustMethod.name, false)}OperationStatus`));
1623
+ const statusType = this.typeToWireType(this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${utils.pascalCase(rustMethod.name, false)}OperationStatus`));
1499
1624
  if (statusType.kind !== 'model') {
1500
1625
  throw new AdapterError('InternalError', `status type for an LRO method '${method.name}' is not a model`, (_p = method.__raw) === null || _p === void 0 ? void 0 : _p.node);
1501
1626
  }
@@ -1532,7 +1657,7 @@ export class Adapter {
1532
1657
  else if (responseHeaders.length > 0) {
1533
1658
  // for methods that don't return a modeled type but return headers,
1534
1659
  // we need to return a marker type
1535
- const markerType = new rust.MarkerType(`${rustClient.name}${codegen.pascalCase(method.name, false)}Result`, rustMethod.visibility);
1660
+ const markerType = new rust.MarkerType(`${rustClient.name}${utils.pascalCase(method.name, false)}Result`, rustMethod.visibility);
1536
1661
  markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
1537
1662
  this.crate.models.push(markerType);
1538
1663
  let resultType;
@@ -1599,10 +1724,11 @@ export class Adapter {
1599
1724
  if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
1600
1725
  throw new AdapterError('InternalError', `unexpected response header collection ${header.serializedName}`, header.__raw.node);
1601
1726
  }
1602
- responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), lowerCasedHeader);
1727
+ responseHeader = new rust.ResponseHeaderHashMap(utils.snakeCaseName(header.name), lowerCasedHeader);
1603
1728
  }
1604
1729
  else {
1605
- responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(lowerCasedHeader), this.typeToWireType(this.getType(header.type)));
1730
+ const headerType = lowerCasedHeader.match(/^etag$/) ? this.getEtag() : this.typeToWireType(this.getType(header.type));
1731
+ responseHeader = new rust.ResponseHeaderScalar(utils.snakeCaseName(header.name), utils.fixETagName(lowerCasedHeader), headerType);
1606
1732
  }
1607
1733
  responseHeader.docs = this.adaptDocs(header.summary, header.doc);
1608
1734
  responseHeadersMap.set(header, responseHeader);
@@ -1640,13 +1766,13 @@ export class Adapter {
1640
1766
  case 'ref':
1641
1767
  return `Ref${recursiveTypeName(type.type)}`;
1642
1768
  case 'scalar':
1643
- return codegen.capitalize(type.type);
1769
+ return utils.capitalize(type.type);
1644
1770
  case 'slice':
1645
1771
  return `Slice${recursiveTypeName(type.type)}`;
1646
1772
  case 'Vec':
1647
1773
  return `${type.kind}${recursiveTypeName(type.type)}`;
1648
1774
  default:
1649
- return codegen.capitalize(type.kind);
1775
+ return utils.capitalize(type.kind);
1650
1776
  }
1651
1777
  };
1652
1778
  const throwOnUnitType = function (type) {
@@ -1832,7 +1958,7 @@ export class Adapter {
1832
1958
  }
1833
1959
  return param.name;
1834
1960
  };
1835
- const paramName = naming.getEscapedReservedName(snakeCaseName(getCorrespondingClientParamName(param)), 'param', reservedParams);
1961
+ const paramName = naming.getEscapedReservedName(utils.snakeCaseName(getCorrespondingClientParamName(param)), 'param', reservedParams);
1836
1962
  let paramType = this.getType(param.type);
1837
1963
  // for required header/path/query method string params, we might emit them as borrowed types
1838
1964
  if (!param.optional && !param.onClient && (param.kind === 'header' || param.kind === 'path' || param.kind === 'query')) {
@@ -2087,9 +2213,9 @@ export class Adapter {
2087
2213
  if (payloadType.kind !== 'model') {
2088
2214
  throw new AdapterError('InternalError', `unexpected kind ${payloadType.kind} for spread body param`, (_b = opParamType.__raw) === null || _b === void 0 ? void 0 : _b.node);
2089
2215
  }
2090
- const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
2216
+ const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param');
2091
2217
  const paramLoc = 'method';
2092
- const formatType = shared.getPayloadFormatType(format);
2218
+ const formatType = utils.getPayloadFormatType(format);
2093
2219
  const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, new rust.Payload(payloadType, format), formatType));
2094
2220
  return adaptedParam;
2095
2221
  }
@@ -2140,45 +2266,11 @@ export class Adapter {
2140
2266
  }
2141
2267
  /** type guard to determine if type is a Ref<HashMap> */
2142
2268
  function isRefHashMap(type) {
2143
- return shared.asTypeOf(type, 'hashmap', 'ref') !== undefined;
2269
+ return utils.asTypeOf(type, 'hashmap', 'ref') !== undefined;
2144
2270
  }
2145
2271
  /** type guard to determine if type is a Ref<Slice> */
2146
2272
  function isRefSlice(type) {
2147
- return shared.asTypeOf(type, 'slice', 'ref') !== undefined;
2148
- }
2149
- /**
2150
- * transforms Etag etc to all lower case.
2151
- * this is to prevent inadvertently snake-casing
2152
- * Etag to e_tag.
2153
- *
2154
- * if name isn't some variant of Etag the
2155
- * original value is returned.
2156
- *
2157
- * @param name the name to transform
2158
- * @returns etag or the original value
2159
- */
2160
- function fixETagName(name) {
2161
- return name.match(/^etag$/i) ? 'etag' : name;
2162
- }
2163
- /**
2164
- * removes any illegal characters from the provided name.
2165
- * note that characters _ and - are preserved so that the
2166
- * proper snake-casing can be performed.
2167
- *
2168
- * @param name the name to transform
2169
- * @returns the transformed name or the original value
2170
- */
2171
- function removeIllegalChars(name) {
2172
- return name.replace(/[!@#$%^&*()+=]/g, '');
2173
- }
2174
- /**
2175
- * snake-cases the provided name
2176
- *
2177
- * @param name the name to snake-case
2178
- * @returns name in snake-case format
2179
- */
2180
- function snakeCaseName(name) {
2181
- return codegen.deconstruct(fixETagName(removeIllegalChars(name))).join('_');
2273
+ return utils.asTypeOf(type, 'slice', 'ref') !== undefined;
2182
2274
  }
2183
2275
  /**
2184
2276
  * recursively creates a map key from the specified type.
@@ -2208,6 +2300,7 @@ function recursiveKeyName(root, type) {
2208
2300
  return recursiveKeyName(`${root}-${type.kind}`, type.type);
2209
2301
  case 'offsetDateTime':
2210
2302
  return `${root}-${type.kind}-${type.encoding}${type.utc ? '-utc' : ''}`;
2303
+ case 'discriminatedUnion':
2211
2304
  case 'model':
2212
2305
  case 'struct':
2213
2306
  return `${root}-${type.kind}-${type.name}`;
@@ -2269,7 +2362,7 @@ function getXMLKind(decorators, field) {
2269
2362
  case 'TypeSpec.Xml.@attribute':
2270
2363
  return 'attribute';
2271
2364
  case 'TypeSpec.Xml.@unwrapped': {
2272
- const fieldType = shared.unwrapOption(field.type);
2365
+ const fieldType = utils.unwrapOption(field.type);
2273
2366
  switch (fieldType.kind) {
2274
2367
  case 'Vec':
2275
2368
  return 'unwrappedList';