@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.
- package/dist/src/codegen/clients.d.ts.map +1 -1
- package/dist/src/codegen/clients.js +120 -93
- 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 +5 -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 +38 -18
- 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 +4 -2
- 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 +170 -70
- 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/client.d.ts +5 -3
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +1 -1
- package/dist/src/codemodel/client.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/customizations.d.ts +12 -0
- package/dist/src/codemodel/customizations.d.ts.map +1 -0
- package/dist/src/codemodel/customizations.js +13 -0
- package/dist/src/codemodel/customizations.js.map +1 -0
- package/dist/src/codemodel/index.d.ts +1 -0
- package/dist/src/codemodel/index.d.ts.map +1 -1
- package/dist/src/codemodel/index.js +1 -0
- package/dist/src/codemodel/index.js.map +1 -1
- package/dist/src/codemodel/types.d.ts +38 -4
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +24 -0
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +7 -2
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +266 -173
- 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 +16 -19
- 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) {
|
|
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
|
-
|
|
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,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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
884
|
-
|
|
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
|
|
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: (
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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}${
|
|
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',
|
|
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 =
|
|
1294
|
-
return
|
|
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
|
-
})
|
|
1298
|
-
})
|
|
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,
|
|
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}${
|
|
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}${
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
2365
|
+
const fieldType = utils.unwrapOption(field.type);
|
|
2273
2366
|
switch (fieldType.kind) {
|
|
2274
2367
|
case 'Vec':
|
|
2275
2368
|
return 'unwrappedList';
|