@azure-tools/typespec-rust 0.33.0 → 0.34.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.
- package/dist/src/codegen/clients.d.ts.map +1 -1
- package/dist/src/codegen/clients.js +118 -91
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/codeGenerator.js +16 -16
- package/dist/src/codegen/codeGenerator.js.map +1 -1
- package/dist/src/codegen/context.js +1 -1
- package/dist/src/codegen/context.js.map +1 -1
- package/dist/src/codegen/enums.d.ts +5 -5
- package/dist/src/codegen/enums.d.ts.map +1 -1
- package/dist/src/codegen/enums.js +10 -6
- package/dist/src/codegen/enums.js.map +1 -1
- package/dist/src/codegen/headerTraits.d.ts.map +1 -1
- package/dist/src/codegen/headerTraits.js +4 -3
- package/dist/src/codegen/headerTraits.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts +23 -0
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +37 -17
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/mod.d.ts.map +1 -1
- package/dist/src/codegen/mod.js +3 -1
- package/dist/src/codegen/mod.js.map +1 -1
- package/dist/src/codegen/models.d.ts +4 -6
- package/dist/src/codegen/models.d.ts.map +1 -1
- package/dist/src/codegen/models.js +129 -61
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/unions.d.ts +8 -6
- package/dist/src/codegen/unions.d.ts.map +1 -1
- package/dist/src/codegen/unions.js +142 -9
- package/dist/src/codegen/unions.js.map +1 -1
- package/dist/src/codegen/use.d.ts +1 -19
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +7 -41
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/crate.d.ts +1 -1
- package/dist/src/codemodel/crate.d.ts.map +1 -1
- package/dist/src/codemodel/types.d.ts +35 -4
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +23 -0
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +5 -2
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +208 -155
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/tcgcadapter/naming.d.ts.map +1 -1
- package/dist/src/tcgcadapter/naming.js +2 -2
- package/dist/src/tcgcadapter/naming.js.map +1 -1
- package/dist/src/utils/utils.d.ts +99 -0
- package/dist/src/utils/utils.d.ts.map +1 -0
- package/dist/src/utils/utils.js +288 -0
- package/dist/src/utils/utils.js.map +1 -0
- package/package.json +5 -8
- package/dist/src/shared/shared.d.ts +0 -29
- package/dist/src/shared/shared.d.ts.map +0 -1
- package/dist/src/shared/shared.js +0 -109
- 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
|
|
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 ||
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 = '
|
|
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(
|
|
364
|
-
var _a, _b, _c, _d;
|
|
365
|
-
if (
|
|
366
|
-
throw new AdapterError('InternalError', 'unnamed union', (_a =
|
|
367
|
-
}
|
|
368
|
-
const unionName =
|
|
369
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (
|
|
390
|
-
|
|
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
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
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) {
|
|
488
|
+
getModelField(modelFlags, property, modelVisibility, stack) {
|
|
427
489
|
var _a;
|
|
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
|
-
|
|
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
|
|
435
|
-
|
|
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 =
|
|
443
|
-
if (unwrappedType.kind === 'literal') {
|
|
444
|
-
|
|
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,23 @@ 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
|
+
}
|
|
466
558
|
return modelField;
|
|
467
559
|
}
|
|
468
560
|
/**
|
|
469
561
|
* converts a tcgc type to a Rust type
|
|
470
562
|
*
|
|
471
563
|
* @param type the tcgc type to convert
|
|
472
|
-
* @param stack is a stack of
|
|
564
|
+
* @param stack is a stack of types used to detect recursive type definitions
|
|
473
565
|
* @returns the adapted Rust type
|
|
474
566
|
*/
|
|
475
567
|
getType(type, stack) {
|
|
476
568
|
var _a, _b, _c;
|
|
569
|
+
if (type.external) {
|
|
570
|
+
return this.getExternalType(type.external);
|
|
571
|
+
}
|
|
477
572
|
const getDateTimeEncoding = (encoding) => {
|
|
478
573
|
var _a;
|
|
479
574
|
switch (encoding) {
|
|
@@ -549,6 +644,9 @@ export class Adapter {
|
|
|
549
644
|
if (type.external) {
|
|
550
645
|
return this.getExternalType(type.external);
|
|
551
646
|
}
|
|
647
|
+
else if (type.discriminatedSubtypes) {
|
|
648
|
+
return this.getDiscriminatedUnion(type);
|
|
649
|
+
}
|
|
552
650
|
return this.getModel(type, stack);
|
|
553
651
|
case 'endpoint':
|
|
554
652
|
case 'plainDate':
|
|
@@ -595,16 +693,8 @@ export class Adapter {
|
|
|
595
693
|
}
|
|
596
694
|
return safeint;
|
|
597
695
|
}
|
|
598
|
-
case 'unknown':
|
|
599
|
-
|
|
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
|
-
}
|
|
696
|
+
case 'unknown':
|
|
697
|
+
return this.getUnknownValue();
|
|
608
698
|
case 'utcDateTime': {
|
|
609
699
|
const encoding = getDateTimeEncoding(type.encode);
|
|
610
700
|
const keyName = `offsetDateTime-${encoding}-utc`;
|
|
@@ -658,6 +748,17 @@ export class Adapter {
|
|
|
658
748
|
this.types.set(keyName, hashmapType);
|
|
659
749
|
return hashmapType;
|
|
660
750
|
}
|
|
751
|
+
/** returns an azure_core::Value */
|
|
752
|
+
getUnknownValue() {
|
|
753
|
+
const keyName = 'jsonValue';
|
|
754
|
+
let anyType = this.types.get(keyName);
|
|
755
|
+
if (anyType) {
|
|
756
|
+
return anyType;
|
|
757
|
+
}
|
|
758
|
+
anyType = new rust.JsonValue(this.crate);
|
|
759
|
+
this.types.set(keyName, anyType);
|
|
760
|
+
return anyType;
|
|
761
|
+
}
|
|
661
762
|
/** returns an Option<T> where T is specified in type */
|
|
662
763
|
getOptionType(type) {
|
|
663
764
|
const typeKey = recursiveKeyName('option', type);
|
|
@@ -853,7 +954,7 @@ export class Adapter {
|
|
|
853
954
|
* @returns a Rust client
|
|
854
955
|
*/
|
|
855
956
|
recursiveAdaptClient(client, parent) {
|
|
856
|
-
var _a, _b, _c, _d;
|
|
957
|
+
var _a, _b, _c, _d, _e;
|
|
857
958
|
let clientName = client.name;
|
|
858
959
|
// NOTE: if the client has the @clientName decorator applied then use that verbatim
|
|
859
960
|
if (parent && !hasClientNameDecorator(client.decorators)) {
|
|
@@ -977,7 +1078,7 @@ export class Adapter {
|
|
|
977
1078
|
// note that the types of the param and the field are different.
|
|
978
1079
|
// we default to "endpoint" and will use the defined name IFF
|
|
979
1080
|
// it has the @clientName decorator applied
|
|
980
|
-
const endpointName = hasClientNameDecorator(templateArg.decorators) ? snakeCaseName(templateArg.name) : 'endpoint';
|
|
1081
|
+
const endpointName = hasClientNameDecorator(templateArg.decorators) ? utils.snakeCaseName(templateArg.name) : 'endpoint';
|
|
981
1082
|
const adaptedParam = new rust.ClientEndpointParameter(endpointName);
|
|
982
1083
|
adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
|
|
983
1084
|
ctorParams.push(adaptedParam);
|
|
@@ -1032,7 +1133,7 @@ export class Adapter {
|
|
|
1032
1133
|
// to create a child client that will need to inherit our client params.
|
|
1033
1134
|
rustClient.endpoint = parent.endpoint;
|
|
1034
1135
|
for (const prop of client.clientInitialization.parameters) {
|
|
1035
|
-
const name = snakeCaseName(prop.name);
|
|
1136
|
+
const name = utils.snakeCaseName(prop.name);
|
|
1036
1137
|
const parentField = parent.fields.find((v) => v.name === name);
|
|
1037
1138
|
if (parentField) {
|
|
1038
1139
|
rustClient.fields.push(parentField);
|
|
@@ -1050,7 +1151,7 @@ export class Adapter {
|
|
|
1050
1151
|
else {
|
|
1051
1152
|
throw new AdapterError('InternalError', `uninstantiatable client ${client.name} has no parent`);
|
|
1052
1153
|
}
|
|
1053
|
-
for (const child of
|
|
1154
|
+
for (const child of (_c = client.children) !== null && _c !== void 0 ? _c : []) {
|
|
1054
1155
|
const subClient = this.recursiveAdaptClient(child, rustClient);
|
|
1055
1156
|
this.adaptClientAccessor(client, child, rustClient, subClient);
|
|
1056
1157
|
}
|
|
@@ -1062,7 +1163,7 @@ export class Adapter {
|
|
|
1062
1163
|
code: 'LroPagingNotSupported',
|
|
1063
1164
|
severity: 'warning',
|
|
1064
1165
|
message: `skip emitting Paging LRO ${method.name}`,
|
|
1065
|
-
target: (
|
|
1166
|
+
target: (_e = (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : tsp.NoTarget,
|
|
1066
1167
|
});
|
|
1067
1168
|
continue;
|
|
1068
1169
|
}
|
|
@@ -1106,22 +1207,8 @@ export class Adapter {
|
|
|
1106
1207
|
* @returns the Rust client parameter
|
|
1107
1208
|
*/
|
|
1108
1209
|
adaptClientParameter(param, constructable) {
|
|
1109
|
-
let paramType;
|
|
1110
|
-
|
|
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);
|
|
1210
|
+
let paramType = param.isApiVersionParam ? this.getStringType() : this.getType(param.type);
|
|
1211
|
+
const paramName = utils.snakeCaseName(param.name);
|
|
1125
1212
|
let optional = false;
|
|
1126
1213
|
// client-side default value makes the param optional
|
|
1127
1214
|
if (param.optional || param.clientDefaultValue) {
|
|
@@ -1157,7 +1244,7 @@ export class Adapter {
|
|
|
1157
1244
|
* @param subClient the sub-client type that the method returns
|
|
1158
1245
|
*/
|
|
1159
1246
|
adaptClientAccessor(parentClient, childClient, rustClient, subClient) {
|
|
1160
|
-
const clientAccessor = new rust.ClientAccessor(`get_${snakeCaseName(subClient.name)}`, rustClient, subClient);
|
|
1247
|
+
const clientAccessor = new rust.ClientAccessor(`get_${utils.snakeCaseName(subClient.name)}`, rustClient, subClient);
|
|
1161
1248
|
clientAccessor.docs.summary = `Returns a new instance of ${subClient.name}.`;
|
|
1162
1249
|
for (const param of childClient.clientInitialization.parameters) {
|
|
1163
1250
|
// check if the client's initializer already has this parameter.
|
|
@@ -1173,7 +1260,7 @@ export class Adapter {
|
|
|
1173
1260
|
if (existsOnParent) {
|
|
1174
1261
|
continue;
|
|
1175
1262
|
}
|
|
1176
|
-
const adaptedParam = new rust.Parameter(snakeCaseName(param.name), this.getType(param.type));
|
|
1263
|
+
const adaptedParam = new rust.Parameter(utils.snakeCaseName(param.name), this.getType(param.type));
|
|
1177
1264
|
adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
|
|
1178
1265
|
clientAccessor.params.push(adaptedParam);
|
|
1179
1266
|
}
|
|
@@ -1189,14 +1276,14 @@ export class Adapter {
|
|
|
1189
1276
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
1190
1277
|
let srcMethodName = method.name;
|
|
1191
1278
|
if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
|
|
1192
|
-
const chunks =
|
|
1279
|
+
const chunks = utils.deconstruct(srcMethodName);
|
|
1193
1280
|
if (chunks[0] === 'get') {
|
|
1194
1281
|
chunks[0] = 'list';
|
|
1195
1282
|
}
|
|
1196
1283
|
else {
|
|
1197
1284
|
chunks.unshift('list');
|
|
1198
1285
|
}
|
|
1199
|
-
srcMethodName =
|
|
1286
|
+
srcMethodName = utils.camelCase(chunks);
|
|
1200
1287
|
this.ctx.program.reportDiagnostic({
|
|
1201
1288
|
code: 'PagingMethodRename',
|
|
1202
1289
|
severity: 'warning',
|
|
@@ -1205,7 +1292,8 @@ export class Adapter {
|
|
|
1205
1292
|
});
|
|
1206
1293
|
}
|
|
1207
1294
|
const languageIndependentName = method.crossLanguageDefinitionId;
|
|
1208
|
-
const methodName = naming.getEscapedReservedName(snakeCaseName(srcMethodName), 'fn');
|
|
1295
|
+
const methodName = naming.getEscapedReservedName(utils.snakeCaseName(srcMethodName), 'fn');
|
|
1296
|
+
const pub = adaptAccessFlags(method.access);
|
|
1209
1297
|
if (srcMethodName !== method.name) {
|
|
1210
1298
|
// if the method was renamed then ensure it doesn't collide
|
|
1211
1299
|
for (const existingMethod of rustClient.methods) {
|
|
@@ -1215,7 +1303,7 @@ export class Adapter {
|
|
|
1215
1303
|
}
|
|
1216
1304
|
}
|
|
1217
1305
|
const optionsLifetime = new rust.Lifetime('a');
|
|
1218
|
-
const methodOptionsStruct = new rust.Struct(`${rustClient.name}${
|
|
1306
|
+
const methodOptionsStruct = new rust.Struct(`${rustClient.name}${utils.pascalCase(srcMethodName, false)}Options`, pub);
|
|
1219
1307
|
methodOptionsStruct.lifetime = optionsLifetime;
|
|
1220
1308
|
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1221
1309
|
let clientMethodOptions;
|
|
@@ -1230,10 +1318,9 @@ export class Adapter {
|
|
|
1230
1318
|
default:
|
|
1231
1319
|
clientMethodOptions = new rust.ClientMethodOptions(this.crate, optionsLifetime);
|
|
1232
1320
|
}
|
|
1233
|
-
const methodOptionsField = new rust.StructField('method_options',
|
|
1321
|
+
const methodOptionsField = new rust.StructField('method_options', pub, clientMethodOptions);
|
|
1234
1322
|
methodOptionsField.docs.summary = 'Allows customization of the method call.';
|
|
1235
1323
|
methodOptionsStruct.fields.push(methodOptionsField);
|
|
1236
|
-
const pub = adaptAccessFlags(method.access);
|
|
1237
1324
|
const methodOptions = new rust.MethodOptions(methodOptionsStruct);
|
|
1238
1325
|
const httpMethod = method.operation.verb;
|
|
1239
1326
|
let rustMethod;
|
|
@@ -1290,12 +1377,11 @@ export class Adapter {
|
|
|
1290
1377
|
// most params have a one-to-one mapping. however, for spread params, there will
|
|
1291
1378
|
// be a many-to-one mapping. i.e. multiple params will map to the same underlying
|
|
1292
1379
|
// operation param. each param corresponds to a field within the operation param.
|
|
1293
|
-
const opParam =
|
|
1294
|
-
return
|
|
1295
|
-
.where((methodParam) => {
|
|
1380
|
+
const opParam = allOpParams.find((opParam) => {
|
|
1381
|
+
return opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).some((methodParam) => {
|
|
1296
1382
|
return methodParam.name === param.name;
|
|
1297
|
-
})
|
|
1298
|
-
})
|
|
1383
|
+
});
|
|
1384
|
+
});
|
|
1299
1385
|
if (!opParam) {
|
|
1300
1386
|
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
1387
|
}
|
|
@@ -1344,7 +1430,7 @@ export class Adapter {
|
|
|
1344
1430
|
else {
|
|
1345
1431
|
fieldType = this.getOptionType(adaptedParam.type);
|
|
1346
1432
|
}
|
|
1347
|
-
const optionsField = new rust.StructField(adaptedParam.name,
|
|
1433
|
+
const optionsField = new rust.StructField(adaptedParam.name, pub, fieldType);
|
|
1348
1434
|
optionsField.docs = adaptedParam.docs;
|
|
1349
1435
|
rustMethod.options.type.fields.push(optionsField);
|
|
1350
1436
|
}
|
|
@@ -1495,7 +1581,7 @@ export class Adapter {
|
|
|
1495
1581
|
}
|
|
1496
1582
|
else if (method.kind === 'lro') {
|
|
1497
1583
|
const format = responseFormat === 'NoFormat' ? 'JsonFormat' : responseFormat;
|
|
1498
|
-
const statusType = this.typeToWireType(this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${
|
|
1584
|
+
const statusType = this.typeToWireType(this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${utils.pascalCase(rustMethod.name, false)}OperationStatus`));
|
|
1499
1585
|
if (statusType.kind !== 'model') {
|
|
1500
1586
|
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
1587
|
}
|
|
@@ -1532,7 +1618,7 @@ export class Adapter {
|
|
|
1532
1618
|
else if (responseHeaders.length > 0) {
|
|
1533
1619
|
// for methods that don't return a modeled type but return headers,
|
|
1534
1620
|
// we need to return a marker type
|
|
1535
|
-
const markerType = new rust.MarkerType(`${rustClient.name}${
|
|
1621
|
+
const markerType = new rust.MarkerType(`${rustClient.name}${utils.pascalCase(method.name, false)}Result`, rustMethod.visibility);
|
|
1536
1622
|
markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1537
1623
|
this.crate.models.push(markerType);
|
|
1538
1624
|
let resultType;
|
|
@@ -1599,10 +1685,10 @@ export class Adapter {
|
|
|
1599
1685
|
if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
|
|
1600
1686
|
throw new AdapterError('InternalError', `unexpected response header collection ${header.serializedName}`, header.__raw.node);
|
|
1601
1687
|
}
|
|
1602
|
-
responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), lowerCasedHeader);
|
|
1688
|
+
responseHeader = new rust.ResponseHeaderHashMap(utils.snakeCaseName(header.name), lowerCasedHeader);
|
|
1603
1689
|
}
|
|
1604
1690
|
else {
|
|
1605
|
-
responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(lowerCasedHeader), this.typeToWireType(this.getType(header.type)));
|
|
1691
|
+
responseHeader = new rust.ResponseHeaderScalar(utils.snakeCaseName(header.name), utils.fixETagName(lowerCasedHeader), this.typeToWireType(this.getType(header.type)));
|
|
1606
1692
|
}
|
|
1607
1693
|
responseHeader.docs = this.adaptDocs(header.summary, header.doc);
|
|
1608
1694
|
responseHeadersMap.set(header, responseHeader);
|
|
@@ -1640,13 +1726,13 @@ export class Adapter {
|
|
|
1640
1726
|
case 'ref':
|
|
1641
1727
|
return `Ref${recursiveTypeName(type.type)}`;
|
|
1642
1728
|
case 'scalar':
|
|
1643
|
-
return
|
|
1729
|
+
return utils.capitalize(type.type);
|
|
1644
1730
|
case 'slice':
|
|
1645
1731
|
return `Slice${recursiveTypeName(type.type)}`;
|
|
1646
1732
|
case 'Vec':
|
|
1647
1733
|
return `${type.kind}${recursiveTypeName(type.type)}`;
|
|
1648
1734
|
default:
|
|
1649
|
-
return
|
|
1735
|
+
return utils.capitalize(type.kind);
|
|
1650
1736
|
}
|
|
1651
1737
|
};
|
|
1652
1738
|
const throwOnUnitType = function (type) {
|
|
@@ -1832,7 +1918,7 @@ export class Adapter {
|
|
|
1832
1918
|
}
|
|
1833
1919
|
return param.name;
|
|
1834
1920
|
};
|
|
1835
|
-
const paramName = naming.getEscapedReservedName(snakeCaseName(getCorrespondingClientParamName(param)), 'param', reservedParams);
|
|
1921
|
+
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(getCorrespondingClientParamName(param)), 'param', reservedParams);
|
|
1836
1922
|
let paramType = this.getType(param.type);
|
|
1837
1923
|
// for required header/path/query method string params, we might emit them as borrowed types
|
|
1838
1924
|
if (!param.optional && !param.onClient && (param.kind === 'header' || param.kind === 'path' || param.kind === 'query')) {
|
|
@@ -2087,9 +2173,9 @@ export class Adapter {
|
|
|
2087
2173
|
if (payloadType.kind !== 'model') {
|
|
2088
2174
|
throw new AdapterError('InternalError', `unexpected kind ${payloadType.kind} for spread body param`, (_b = opParamType.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
2089
2175
|
}
|
|
2090
|
-
const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
|
|
2176
|
+
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param');
|
|
2091
2177
|
const paramLoc = 'method';
|
|
2092
|
-
const formatType =
|
|
2178
|
+
const formatType = utils.getPayloadFormatType(format);
|
|
2093
2179
|
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
2180
|
return adaptedParam;
|
|
2095
2181
|
}
|
|
@@ -2140,45 +2226,11 @@ export class Adapter {
|
|
|
2140
2226
|
}
|
|
2141
2227
|
/** type guard to determine if type is a Ref<HashMap> */
|
|
2142
2228
|
function isRefHashMap(type) {
|
|
2143
|
-
return
|
|
2229
|
+
return utils.asTypeOf(type, 'hashmap', 'ref') !== undefined;
|
|
2144
2230
|
}
|
|
2145
2231
|
/** type guard to determine if type is a Ref<Slice> */
|
|
2146
2232
|
function isRefSlice(type) {
|
|
2147
|
-
return
|
|
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('_');
|
|
2233
|
+
return utils.asTypeOf(type, 'slice', 'ref') !== undefined;
|
|
2182
2234
|
}
|
|
2183
2235
|
/**
|
|
2184
2236
|
* recursively creates a map key from the specified type.
|
|
@@ -2208,6 +2260,7 @@ function recursiveKeyName(root, type) {
|
|
|
2208
2260
|
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
2209
2261
|
case 'offsetDateTime':
|
|
2210
2262
|
return `${root}-${type.kind}-${type.encoding}${type.utc ? '-utc' : ''}`;
|
|
2263
|
+
case 'discriminatedUnion':
|
|
2211
2264
|
case 'model':
|
|
2212
2265
|
case 'struct':
|
|
2213
2266
|
return `${root}-${type.kind}-${type.name}`;
|
|
@@ -2269,7 +2322,7 @@ function getXMLKind(decorators, field) {
|
|
|
2269
2322
|
case 'TypeSpec.Xml.@attribute':
|
|
2270
2323
|
return 'attribute';
|
|
2271
2324
|
case 'TypeSpec.Xml.@unwrapped': {
|
|
2272
|
-
const fieldType =
|
|
2325
|
+
const fieldType = utils.unwrapOption(field.type);
|
|
2273
2326
|
switch (fieldType.kind) {
|
|
2274
2327
|
case 'Vec':
|
|
2275
2328
|
return 'unwrappedList';
|