@azure-tools/typespec-rust 0.13.2 → 0.14.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 +155 -85
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/context.d.ts.map +1 -1
- package/dist/src/codegen/context.js +3 -2
- package/dist/src/codegen/context.js.map +1 -1
- package/dist/src/codegen/errors.d.ts +13 -0
- package/dist/src/codegen/errors.d.ts.map +1 -0
- package/dist/src/codegen/errors.js +15 -0
- package/dist/src/codegen/errors.js.map +1 -0
- package/dist/src/codegen/headerTraits.d.ts.map +1 -1
- package/dist/src/codegen/headerTraits.js +3 -2
- package/dist/src/codegen/headerTraits.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +13 -3
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/models.d.ts.map +1 -1
- package/dist/src/codegen/models.js +133 -71
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +4 -2
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +17 -31
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +1 -35
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/method.d.ts +0 -2
- package/dist/src/codemodel/method.d.ts.map +1 -1
- package/dist/src/codemodel/method.js +0 -1
- package/dist/src/codemodel/method.js.map +1 -1
- package/dist/src/codemodel/types.d.ts +52 -5
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +26 -7
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +84 -34
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.d.ts +37 -4
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +27 -2
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +68 -11
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +421 -190
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/tcgcadapter/helpers.d.ts +8 -0
- package/dist/src/tcgcadapter/helpers.d.ts.map +1 -1
- package/dist/src/tcgcadapter/helpers.js +13 -0
- package/dist/src/tcgcadapter/helpers.js.map +1 -1
- package/package.json +22 -24
|
@@ -9,6 +9,18 @@ import * as helpers from './helpers.js';
|
|
|
9
9
|
import * as naming from './naming.js';
|
|
10
10
|
import * as tcgc from '@azure-tools/typespec-client-generator-core';
|
|
11
11
|
import * as rust from '../codemodel/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* AdapterError is thrown when the emitter fails to convert part of the tcgc code
|
|
14
|
+
* model to the emitter code model. this could be due to the emitter not supporting
|
|
15
|
+
* some tsp construct.
|
|
16
|
+
*/
|
|
17
|
+
export class AdapterError extends Error {
|
|
18
|
+
constructor(code, message, target) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.target = target !== null && target !== void 0 ? target : NoTarget;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
12
24
|
/** Adapter converts the tcgc code model to a Rust Crate */
|
|
13
25
|
export class Adapter {
|
|
14
26
|
/**
|
|
@@ -135,6 +147,7 @@ export class Adapter {
|
|
|
135
147
|
* @returns a Rust enum value
|
|
136
148
|
*/
|
|
137
149
|
getEnumValue(sdkEnumValue) {
|
|
150
|
+
var _a;
|
|
138
151
|
const enumType = this.getEnum(sdkEnumValue.enumType);
|
|
139
152
|
// find the specified enum value
|
|
140
153
|
for (const value of enumType.values) {
|
|
@@ -142,23 +155,31 @@ export class Adapter {
|
|
|
142
155
|
return value;
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
|
-
throw new
|
|
158
|
+
throw new AdapterError('InternalError', `didn't find enum value for name ${sdkEnumValue.name} in enum ${enumType.name}`, (_a = sdkEnumValue.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
146
159
|
}
|
|
147
160
|
/**
|
|
148
161
|
* converts a tcgc model to a Rust model
|
|
149
162
|
*
|
|
150
163
|
* @param model the tcgc model to convert
|
|
164
|
+
* @param stack is a stack of model type names used to detect recursive type definitions
|
|
151
165
|
* @returns a Rust model
|
|
152
166
|
*/
|
|
153
|
-
getModel(model) {
|
|
167
|
+
getModel(model, stack) {
|
|
168
|
+
var _a, _b;
|
|
154
169
|
if (model.name.length === 0) {
|
|
155
|
-
throw new
|
|
170
|
+
throw new AdapterError('InternalError', 'unnamed model', (_a = model.__raw) === null || _a === void 0 ? void 0 : _a.node); // TODO: this might no longer be an issue
|
|
156
171
|
}
|
|
157
172
|
const modelName = codegen.capitalize(model.name);
|
|
158
173
|
let rustModel = this.types.get(modelName);
|
|
159
174
|
if (rustModel) {
|
|
160
175
|
return rustModel;
|
|
161
176
|
}
|
|
177
|
+
// no stack means this is the first model in
|
|
178
|
+
// the chain of potentially recursive calls
|
|
179
|
+
if (!stack) {
|
|
180
|
+
stack = new Array();
|
|
181
|
+
}
|
|
182
|
+
stack.push(modelName);
|
|
162
183
|
let modelFlags = rust.ModelFlags.Unspecified;
|
|
163
184
|
if ((model.usage & tcgc.UsageFlags.Input) === tcgc.UsageFlags.Input) {
|
|
164
185
|
modelFlags |= rust.ModelFlags.Input;
|
|
@@ -202,12 +223,13 @@ export class Adapter {
|
|
|
202
223
|
// we just adapt it here as a regular model field.
|
|
203
224
|
}
|
|
204
225
|
else {
|
|
205
|
-
throw new
|
|
226
|
+
throw new AdapterError('UnsupportedTsp', `model property kind ${property.kind} NYI`, (_b = property.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
206
227
|
}
|
|
207
228
|
}
|
|
208
|
-
const structField = this.getModelField(property, rustModel.visibility);
|
|
229
|
+
const structField = this.getModelField(property, rustModel.visibility, stack);
|
|
209
230
|
rustModel.fields.push(structField);
|
|
210
231
|
}
|
|
232
|
+
stack.pop();
|
|
211
233
|
return rustModel;
|
|
212
234
|
}
|
|
213
235
|
/**
|
|
@@ -215,15 +237,19 @@ export class Adapter {
|
|
|
215
237
|
*
|
|
216
238
|
* @param property the tcgc model property to convert
|
|
217
239
|
* @param modelVisibility the visibility of the model that contains the property
|
|
240
|
+
* @param stack is a stack of model type names used to detect recursive type definitions
|
|
218
241
|
* @returns a Rust model field
|
|
219
242
|
*/
|
|
220
|
-
getModelField(property, modelVisibility) {
|
|
221
|
-
let fieldType = this.getType(property.type);
|
|
243
|
+
getModelField(property, modelVisibility, stack) {
|
|
244
|
+
let fieldType = this.getType(property.type, stack);
|
|
245
|
+
// if the field's type is a model and it's in the type stack then
|
|
246
|
+
// box it. this is to avoid infinitely recursive type definitions.
|
|
247
|
+
if (fieldType.kind === 'model' && stack.includes(fieldType.name)) {
|
|
248
|
+
fieldType = new rust.Box(fieldType);
|
|
249
|
+
}
|
|
222
250
|
// for public models each field is always an Option<T>.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if ((modelVisibility === 'pub' || property.optional) && fieldType.kind !== 'hashmap' && fieldType.kind !== 'Vec') {
|
|
226
|
-
fieldType = new rust.Option(this.typeToWireType(fieldType));
|
|
251
|
+
if (modelVisibility === 'pub' || property.optional) {
|
|
252
|
+
fieldType = new rust.Option(fieldType.kind === 'box' ? fieldType : this.typeToWireType(fieldType));
|
|
227
253
|
}
|
|
228
254
|
const modelField = new rust.ModelField(naming.getEscapedReservedName(snakeCaseName(property.name), 'prop'), property.serializedName, modelVisibility, fieldType);
|
|
229
255
|
modelField.docs = this.adaptDocs(property.summary, property.doc);
|
|
@@ -239,8 +265,15 @@ export class Adapter {
|
|
|
239
265
|
}
|
|
240
266
|
return modelField;
|
|
241
267
|
}
|
|
242
|
-
/**
|
|
243
|
-
|
|
268
|
+
/**
|
|
269
|
+
* converts a tcgc type to a Rust type
|
|
270
|
+
*
|
|
271
|
+
* @param type the tcgc type to convert
|
|
272
|
+
* @param stack is a stack of model type names used to detect recursive type definitions
|
|
273
|
+
* @returns the adapted Rust type
|
|
274
|
+
*/
|
|
275
|
+
getType(type, stack) {
|
|
276
|
+
var _a, _b;
|
|
244
277
|
const getDateTimeEncoding = (encoding) => {
|
|
245
278
|
switch (encoding) {
|
|
246
279
|
case 'rfc3339':
|
|
@@ -251,10 +284,6 @@ export class Adapter {
|
|
|
251
284
|
}
|
|
252
285
|
};
|
|
253
286
|
const getScalar = (kind) => {
|
|
254
|
-
let scalar = this.types.get(kind);
|
|
255
|
-
if (scalar) {
|
|
256
|
-
return scalar;
|
|
257
|
-
}
|
|
258
287
|
let scalarType;
|
|
259
288
|
switch (kind) {
|
|
260
289
|
case 'boolean':
|
|
@@ -292,47 +321,32 @@ export class Adapter {
|
|
|
292
321
|
scalarType = 'u8';
|
|
293
322
|
break;
|
|
294
323
|
}
|
|
295
|
-
|
|
296
|
-
this.types.set(kind, scalar);
|
|
297
|
-
return scalar;
|
|
324
|
+
return this.getScalar(scalarType);
|
|
298
325
|
};
|
|
299
326
|
switch (type.kind) {
|
|
300
|
-
case 'array':
|
|
301
|
-
|
|
302
|
-
let vectorType = this.types.get(keyName);
|
|
303
|
-
if (vectorType) {
|
|
304
|
-
return vectorType;
|
|
305
|
-
}
|
|
306
|
-
vectorType = new rust.Vector(this.typeToWireType(this.getType(type.valueType)));
|
|
307
|
-
this.types.set(keyName, vectorType);
|
|
308
|
-
return vectorType;
|
|
309
|
-
}
|
|
327
|
+
case 'array':
|
|
328
|
+
return this.getVec(this.typeToWireType(this.getType(type.valueType, stack)));
|
|
310
329
|
case 'bytes': {
|
|
311
330
|
let encoding = 'std';
|
|
312
331
|
if (type.encode === 'base64url') {
|
|
313
332
|
encoding = 'url';
|
|
314
333
|
}
|
|
315
|
-
|
|
316
|
-
let encodedBytesType = this.types.get(keyName);
|
|
317
|
-
if (encodedBytesType) {
|
|
318
|
-
return encodedBytesType;
|
|
319
|
-
}
|
|
320
|
-
encodedBytesType = new rust.EncodedBytes(encoding);
|
|
321
|
-
this.types.set(keyName, encodedBytesType);
|
|
322
|
-
return encodedBytesType;
|
|
334
|
+
return this.getEncodedBytes(encoding, false);
|
|
323
335
|
}
|
|
324
336
|
case 'constant':
|
|
325
337
|
return this.getLiteral(type);
|
|
326
|
-
case '
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
338
|
+
case 'decimal':
|
|
339
|
+
case 'decimal128': {
|
|
340
|
+
const keyName = 'decimal';
|
|
341
|
+
let decimalType = this.types.get(keyName);
|
|
342
|
+
if (!decimalType) {
|
|
343
|
+
decimalType = new rust.Decimal(this.crate);
|
|
344
|
+
this.types.set(keyName, decimalType);
|
|
331
345
|
}
|
|
332
|
-
|
|
333
|
-
this.types.set(keyName, hashmapType);
|
|
334
|
-
return hashmapType;
|
|
346
|
+
return decimalType;
|
|
335
347
|
}
|
|
348
|
+
case 'dict':
|
|
349
|
+
return this.getHashMap(this.typeToWireType(this.getType(type.valueType, stack)));
|
|
336
350
|
case 'duration':
|
|
337
351
|
switch (type.wireType.kind) {
|
|
338
352
|
case 'float':
|
|
@@ -344,7 +358,7 @@ export class Adapter {
|
|
|
344
358
|
case 'string':
|
|
345
359
|
return this.getStringType();
|
|
346
360
|
default:
|
|
347
|
-
throw new
|
|
361
|
+
throw new AdapterError('UnsupportedTsp', `unhandled duration wireType.kind ${type.wireType.kind}`, (_a = type.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
348
362
|
}
|
|
349
363
|
case 'boolean':
|
|
350
364
|
case 'float32':
|
|
@@ -363,12 +377,12 @@ export class Adapter {
|
|
|
363
377
|
case 'enumvalue':
|
|
364
378
|
return this.getEnumValue(type);
|
|
365
379
|
case 'model':
|
|
366
|
-
return this.getModel(type);
|
|
380
|
+
return this.getModel(type, stack);
|
|
367
381
|
case 'endpoint':
|
|
368
382
|
case 'string':
|
|
369
383
|
case 'url': {
|
|
370
384
|
if (type.kind === 'string' && type.crossLanguageDefinitionId === 'Azure.Core.eTag') {
|
|
371
|
-
const etagKey = '
|
|
385
|
+
const etagKey = 'Etag';
|
|
372
386
|
let etagType = this.types.get(etagKey);
|
|
373
387
|
if (etagType) {
|
|
374
388
|
return etagType;
|
|
@@ -379,39 +393,96 @@ export class Adapter {
|
|
|
379
393
|
}
|
|
380
394
|
return this.getStringType();
|
|
381
395
|
}
|
|
396
|
+
case 'nullable':
|
|
397
|
+
// TODO: workaround until https://github.com/Azure/typespec-rust/issues/42 is fixed
|
|
398
|
+
return this.getType(type.type, stack);
|
|
382
399
|
case 'offsetDateTime': {
|
|
383
|
-
const
|
|
400
|
+
const encoding = getDateTimeEncoding(type.encode);
|
|
401
|
+
const keyName = `offsetDateTime-${encoding}`;
|
|
384
402
|
let timeType = this.types.get(keyName);
|
|
385
403
|
if (timeType) {
|
|
386
404
|
return timeType;
|
|
387
405
|
}
|
|
388
|
-
timeType = new rust.OffsetDateTime(this.crate,
|
|
406
|
+
timeType = new rust.OffsetDateTime(this.crate, encoding, false);
|
|
389
407
|
this.types.set(keyName, timeType);
|
|
390
408
|
return timeType;
|
|
391
409
|
}
|
|
392
410
|
case 'unknown': {
|
|
393
|
-
|
|
411
|
+
const keyName = 'jsonValue';
|
|
412
|
+
let anyType = this.types.get(keyName);
|
|
394
413
|
if (anyType) {
|
|
395
414
|
return anyType;
|
|
396
415
|
}
|
|
397
416
|
anyType = new rust.JsonValue(this.crate);
|
|
398
|
-
this.types.set(
|
|
417
|
+
this.types.set(keyName, anyType);
|
|
399
418
|
return anyType;
|
|
400
419
|
}
|
|
401
420
|
case 'utcDateTime': {
|
|
402
|
-
const
|
|
421
|
+
const encoding = getDateTimeEncoding(type.encode);
|
|
422
|
+
const keyName = `offsetDateTime-${encoding}-utc`;
|
|
403
423
|
let timeType = this.types.get(keyName);
|
|
404
424
|
if (timeType) {
|
|
405
425
|
return timeType;
|
|
406
426
|
}
|
|
407
|
-
timeType = new rust.OffsetDateTime(this.crate,
|
|
427
|
+
timeType = new rust.OffsetDateTime(this.crate, encoding, true);
|
|
408
428
|
this.types.set(keyName, timeType);
|
|
409
429
|
return timeType;
|
|
410
430
|
}
|
|
411
431
|
default:
|
|
412
|
-
throw new
|
|
432
|
+
throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (_b = type.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
413
433
|
}
|
|
414
434
|
}
|
|
435
|
+
/** returns an EncodedBytes type with the specified encoding */
|
|
436
|
+
getEncodedBytes(encoding, asSlice) {
|
|
437
|
+
const keyName = `encodedBytes-${encoding}${asSlice ? '-slice' : ''}`;
|
|
438
|
+
let encodedBytesType = this.types.get(keyName);
|
|
439
|
+
if (encodedBytesType) {
|
|
440
|
+
return encodedBytesType;
|
|
441
|
+
}
|
|
442
|
+
encodedBytesType = new rust.EncodedBytes(encoding, asSlice);
|
|
443
|
+
this.types.set(keyName, encodedBytesType);
|
|
444
|
+
return encodedBytesType;
|
|
445
|
+
}
|
|
446
|
+
/** returns a HashMap<String, type> */
|
|
447
|
+
getHashMap(type) {
|
|
448
|
+
const keyName = recursiveKeyName('hashmap', type);
|
|
449
|
+
let hashmapType = this.types.get(keyName);
|
|
450
|
+
if (hashmapType) {
|
|
451
|
+
return hashmapType;
|
|
452
|
+
}
|
|
453
|
+
hashmapType = new rust.HashMap(type);
|
|
454
|
+
this.types.set(keyName, hashmapType);
|
|
455
|
+
return hashmapType;
|
|
456
|
+
}
|
|
457
|
+
/** returns the specified type wrapped in a Ref */
|
|
458
|
+
getRefType(type) {
|
|
459
|
+
const typeKey = recursiveKeyName('ref', type);
|
|
460
|
+
let refType = this.types.get(typeKey);
|
|
461
|
+
if (!refType) {
|
|
462
|
+
refType = new rust.Ref(type);
|
|
463
|
+
this.types.set(typeKey, refType);
|
|
464
|
+
}
|
|
465
|
+
return refType;
|
|
466
|
+
}
|
|
467
|
+
/** returns a scalar for the specified scalar type */
|
|
468
|
+
getScalar(type) {
|
|
469
|
+
let scalar = this.types.get(type);
|
|
470
|
+
if (!scalar) {
|
|
471
|
+
scalar = new rust.Scalar(type);
|
|
472
|
+
this.types.set(type, scalar);
|
|
473
|
+
}
|
|
474
|
+
return scalar;
|
|
475
|
+
}
|
|
476
|
+
/** returns a slice of the specified type */
|
|
477
|
+
getSlice(type) {
|
|
478
|
+
const typeKey = recursiveKeyName('slice', type);
|
|
479
|
+
let slice = this.types.get(typeKey);
|
|
480
|
+
if (!slice) {
|
|
481
|
+
slice = new rust.Slice(type);
|
|
482
|
+
this.types.set(typeKey, slice);
|
|
483
|
+
}
|
|
484
|
+
return slice;
|
|
485
|
+
}
|
|
415
486
|
/** returns the Rust string slice type */
|
|
416
487
|
getStringSlice() {
|
|
417
488
|
const typeKey = 'str';
|
|
@@ -445,6 +516,17 @@ export class Adapter {
|
|
|
445
516
|
this.types.set(typeKey, unitType);
|
|
446
517
|
return unitType;
|
|
447
518
|
}
|
|
519
|
+
/** returns a Vec<type> */
|
|
520
|
+
getVec(type) {
|
|
521
|
+
const keyName = recursiveKeyName('Vec', type);
|
|
522
|
+
let vectorType = this.types.get(keyName);
|
|
523
|
+
if (vectorType) {
|
|
524
|
+
return vectorType;
|
|
525
|
+
}
|
|
526
|
+
vectorType = new rust.Vector(type);
|
|
527
|
+
this.types.set(keyName, vectorType);
|
|
528
|
+
return vectorType;
|
|
529
|
+
}
|
|
448
530
|
/**
|
|
449
531
|
* converts a tcgc constant to a Rust literal
|
|
450
532
|
*
|
|
@@ -494,7 +576,7 @@ export class Adapter {
|
|
|
494
576
|
* @returns a Rust client
|
|
495
577
|
*/
|
|
496
578
|
recursiveAdaptClient(client, parent) {
|
|
497
|
-
var _a;
|
|
579
|
+
var _a, _b, _c, _d, _e;
|
|
498
580
|
let clientName = client.name;
|
|
499
581
|
// NOTE: if the client has the @clientName decorator applied then use that verbatim
|
|
500
582
|
if (parent && !client.decorators.find((decorator) => decorator.name === 'Azure.ClientGenerator.Core.@clientName')) {
|
|
@@ -561,7 +643,7 @@ export class Adapter {
|
|
|
561
643
|
}
|
|
562
644
|
default:
|
|
563
645
|
if (throwOnDefault) {
|
|
564
|
-
throw new
|
|
646
|
+
throw new AdapterError('UnsupportedTsp', `credential scheme type ${cred.type} NYI`);
|
|
565
647
|
}
|
|
566
648
|
return AuthTypes.Default;
|
|
567
649
|
}
|
|
@@ -585,7 +667,7 @@ export class Adapter {
|
|
|
585
667
|
}
|
|
586
668
|
// no supported credential types were specified
|
|
587
669
|
if (authType === AuthTypes.Default) {
|
|
588
|
-
throw new
|
|
670
|
+
throw new AdapterError('UnsupportedTsp', `credential scheme types ${variantKinds.join()} NYI`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
589
671
|
}
|
|
590
672
|
continue;
|
|
591
673
|
}
|
|
@@ -613,7 +695,7 @@ export class Adapter {
|
|
|
613
695
|
// note that the types of the param and the field are different.
|
|
614
696
|
// NOTE: we use param.name here instead of templateArg.name as
|
|
615
697
|
// the former has the fixed name "endpoint" which is what we want.
|
|
616
|
-
const adaptedParam = new rust.ClientMethodParameter(param.name, this.getStringSlice(), false
|
|
698
|
+
const adaptedParam = new rust.ClientMethodParameter(param.name, this.getRefType(this.getStringSlice()), false);
|
|
617
699
|
adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
|
|
618
700
|
ctorParams.push(adaptedParam);
|
|
619
701
|
rustClient.fields.push(new rust.StructField(param.name, 'pubCrate', new rust.Url(this.crate)));
|
|
@@ -642,9 +724,9 @@ export class Adapter {
|
|
|
642
724
|
}
|
|
643
725
|
const clientParam = this.adaptClientParameter(templateArg, rustClient.constructable);
|
|
644
726
|
if (clientParam.kind !== 'clientEndpoint') {
|
|
645
|
-
throw new
|
|
727
|
+
throw new AdapterError('InternalError', `unexpected client parameter kind ${clientParam.kind}`, (_b = templateArg.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
646
728
|
}
|
|
647
|
-
(
|
|
729
|
+
(_c = rustClient.constructable.endpoint) === null || _c === void 0 ? void 0 : _c.parameters.push(clientParam);
|
|
648
730
|
ctorParams.push(clientParam);
|
|
649
731
|
}
|
|
650
732
|
break;
|
|
@@ -691,7 +773,7 @@ export class Adapter {
|
|
|
691
773
|
}
|
|
692
774
|
}
|
|
693
775
|
else {
|
|
694
|
-
throw new
|
|
776
|
+
throw new AdapterError('InternalError', `uninstantiable client ${client.name} has no parent`);
|
|
695
777
|
}
|
|
696
778
|
for (const child of values(client.children)) {
|
|
697
779
|
const subClient = this.recursiveAdaptClient(child, rustClient);
|
|
@@ -705,7 +787,7 @@ export class Adapter {
|
|
|
705
787
|
code: 'LroNotSupported',
|
|
706
788
|
severity: 'warning',
|
|
707
789
|
message: `skip emitting LRO ${method.name}`,
|
|
708
|
-
target: method.__raw ?
|
|
790
|
+
target: (_e = (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : NoTarget,
|
|
709
791
|
});
|
|
710
792
|
continue;
|
|
711
793
|
}
|
|
@@ -723,12 +805,15 @@ export class Adapter {
|
|
|
723
805
|
*/
|
|
724
806
|
createTokenCredentialCtor(rustClient, cred) {
|
|
725
807
|
if (cred.flows.length === 0) {
|
|
726
|
-
throw new
|
|
808
|
+
throw new AdapterError('InternalError', `no flows defined for credential type ${cred.type}`, cred.model);
|
|
727
809
|
}
|
|
728
810
|
const scopes = new Array();
|
|
729
811
|
for (const scope of cred.flows[0].scopes) {
|
|
730
812
|
scopes.push(scope.value);
|
|
731
813
|
}
|
|
814
|
+
if (scopes.length === 0) {
|
|
815
|
+
throw new AdapterError('InternalError', 'scopes must contain at least one entry', cred.model);
|
|
816
|
+
}
|
|
732
817
|
const ctorTokenCredential = new rust.Constructor('new');
|
|
733
818
|
const tokenCredParam = new rust.ClientMethodParameter('credential', new rust.Arc(new rust.TokenCredential(this.crate, scopes)), false);
|
|
734
819
|
tokenCredParam.docs.summary = `An implementation of [\`TokenCredential\`](azure_core::credentials::TokenCredential) that can provide an Entra ID token to use when authenticating.`;
|
|
@@ -744,10 +829,11 @@ export class Adapter {
|
|
|
744
829
|
* @returns the Rust client parameter
|
|
745
830
|
*/
|
|
746
831
|
adaptClientParameter(param, constructable) {
|
|
832
|
+
var _a;
|
|
747
833
|
let paramType;
|
|
748
834
|
if (param.isApiVersionParam) {
|
|
749
835
|
if (!param.clientDefaultValue) {
|
|
750
|
-
throw new
|
|
836
|
+
throw new AdapterError('InternalError', `API version parameter ${param.name} has no clientDefaultValue`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
751
837
|
}
|
|
752
838
|
paramType = this.getStringType();
|
|
753
839
|
}
|
|
@@ -816,6 +902,7 @@ export class Adapter {
|
|
|
816
902
|
* @param rustClient the client to which the method belongs
|
|
817
903
|
*/
|
|
818
904
|
adaptMethod(method, rustClient) {
|
|
905
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
819
906
|
let srcMethodName = method.name;
|
|
820
907
|
if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
|
|
821
908
|
const chunks = codegen.deconstruct(srcMethodName);
|
|
@@ -826,11 +913,11 @@ export class Adapter {
|
|
|
826
913
|
code: 'PagingMethodRename',
|
|
827
914
|
severity: 'warning',
|
|
828
915
|
message: `renamed paging method from ${method.name} to ${srcMethodName}`,
|
|
829
|
-
target: method.__raw ?
|
|
916
|
+
target: (_b = (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : NoTarget,
|
|
830
917
|
});
|
|
831
918
|
}
|
|
832
919
|
else if (this.renamedMethods.has(srcMethodName)) {
|
|
833
|
-
throw new
|
|
920
|
+
throw new AdapterError('NameCollision', `method name ${srcMethodName} collides with a renamed method`, (_c = method.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
834
921
|
}
|
|
835
922
|
const methodName = naming.getEscapedReservedName(snakeCaseName(srcMethodName), 'fn');
|
|
836
923
|
const optionsLifetime = new rust.Lifetime('a');
|
|
@@ -865,7 +952,7 @@ export class Adapter {
|
|
|
865
952
|
rustMethod = new rust.PageableMethod(methodName, rustClient, pub, methodOptions, httpMethod, httpPath);
|
|
866
953
|
break;
|
|
867
954
|
default:
|
|
868
|
-
throw new
|
|
955
|
+
throw new AdapterError('UnsupportedTsp', `method kind ${method.kind} NYI`, (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
869
956
|
}
|
|
870
957
|
rustMethod.docs = this.adaptDocs(method.summary, method.doc);
|
|
871
958
|
rustClient.methods.push(rustMethod);
|
|
@@ -875,6 +962,8 @@ export class Adapter {
|
|
|
875
962
|
if (method.operation.bodyParam) {
|
|
876
963
|
allOpParams.push(method.operation.bodyParam);
|
|
877
964
|
}
|
|
965
|
+
// maps tcgc method header/query params to their Rust method params
|
|
966
|
+
const paramsMap = new Map();
|
|
878
967
|
for (const param of method.parameters) {
|
|
879
968
|
// we need to translate from the method param to its underlying operation param.
|
|
880
969
|
// most params have a one-to-one mapping. however, for spread params, there will
|
|
@@ -886,7 +975,7 @@ export class Adapter {
|
|
|
886
975
|
}).any();
|
|
887
976
|
}).first();
|
|
888
977
|
if (!opParam) {
|
|
889
|
-
throw new
|
|
978
|
+
throw new AdapterError('InternalError', `didn't find operation parameter for method ${method.name} parameter ${param.name}`, (_e = param.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
890
979
|
}
|
|
891
980
|
let adaptedParam;
|
|
892
981
|
// for spread params there are two cases we need to consider.
|
|
@@ -900,6 +989,9 @@ export class Adapter {
|
|
|
900
989
|
else {
|
|
901
990
|
adaptedParam = this.adaptMethodParameter(opParam);
|
|
902
991
|
}
|
|
992
|
+
if (adaptedParam.kind === 'header' || adaptedParam.kind === 'query') {
|
|
993
|
+
paramsMap.set(param, adaptedParam);
|
|
994
|
+
}
|
|
903
995
|
adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
|
|
904
996
|
rustMethod.params.push(adaptedParam);
|
|
905
997
|
if (adaptedParam.optional) {
|
|
@@ -911,18 +1003,11 @@ export class Adapter {
|
|
|
911
1003
|
// already wrapped in an Option<T> type.
|
|
912
1004
|
const field = adaptedParam.type.content.type.fields.find(f => { return f.name === adaptedParam.name; });
|
|
913
1005
|
if (!field) {
|
|
914
|
-
throw new
|
|
1006
|
+
throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.type.name}`);
|
|
915
1007
|
}
|
|
916
1008
|
fieldType = field.type;
|
|
917
1009
|
}
|
|
918
1010
|
else {
|
|
919
|
-
switch (adaptedParam.type.kind) {
|
|
920
|
-
case 'requestContent':
|
|
921
|
-
case 'struct':
|
|
922
|
-
break;
|
|
923
|
-
default:
|
|
924
|
-
adaptedParam.type = this.typeToWireType(adaptedParam.type);
|
|
925
|
-
}
|
|
926
1011
|
fieldType = new rust.Option(adaptedParam.type);
|
|
927
1012
|
}
|
|
928
1013
|
const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
|
|
@@ -940,17 +1025,18 @@ export class Adapter {
|
|
|
940
1025
|
}
|
|
941
1026
|
}
|
|
942
1027
|
const getBodyFormat = () => {
|
|
1028
|
+
var _a, _b;
|
|
943
1029
|
// fetch the body format from the HTTP responses.
|
|
944
1030
|
// they should all have the same type so no need to match responses to type.
|
|
945
1031
|
let defaultContentType;
|
|
946
1032
|
for (const httpResp of method.operation.responses) {
|
|
947
1033
|
if (defaultContentType && httpResp.defaultContentType && defaultContentType !== httpResp.defaultContentType) {
|
|
948
|
-
throw new
|
|
1034
|
+
throw new AdapterError('InternalError', `method ${method.name} has conflicting content types`, (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
949
1035
|
}
|
|
950
1036
|
defaultContentType = httpResp.defaultContentType;
|
|
951
1037
|
}
|
|
952
1038
|
if (!defaultContentType) {
|
|
953
|
-
throw new
|
|
1039
|
+
throw new AdapterError('InternalError', `unable to determine content type for method ${method.name}`, (_b = method.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
954
1040
|
}
|
|
955
1041
|
return this.adaptBodyFormat(defaultContentType);
|
|
956
1042
|
};
|
|
@@ -980,16 +1066,53 @@ export class Adapter {
|
|
|
980
1066
|
// however, we want the synthesized paged response envelope type instead.
|
|
981
1067
|
const synthesizedType = method.operation.responses[0].type;
|
|
982
1068
|
if (!synthesizedType) {
|
|
983
|
-
throw new
|
|
1069
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no synthesized response type`, (_f = method.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
984
1070
|
}
|
|
985
1071
|
else if (synthesizedType.kind !== 'model') {
|
|
986
|
-
throw new
|
|
1072
|
+
throw new AdapterError('UnsupportedTsp', `paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`, (_g = method.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
987
1073
|
}
|
|
988
1074
|
const format = getBodyFormat();
|
|
989
|
-
const synthesizedModel = this.getModel(synthesizedType);
|
|
1075
|
+
const synthesizedModel = this.getModel(synthesizedType, new Array());
|
|
990
1076
|
if (!this.crate.models.includes(synthesizedModel)) {
|
|
991
1077
|
this.crate.models.push(synthesizedModel);
|
|
992
1078
|
}
|
|
1079
|
+
// for the pager response type, remove the Option<T> around the Vec<T> for the page items
|
|
1080
|
+
if (!method.pagingMetadata.pageItemsSegments) {
|
|
1081
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (_h = method.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
1082
|
+
}
|
|
1083
|
+
// unwrap all of the segments for the paged response
|
|
1084
|
+
let unwrappedCount = 0;
|
|
1085
|
+
let typeToUnwrap = synthesizedModel;
|
|
1086
|
+
for (const pageItemsSegment of method.pagingMetadata.pageItemsSegments) {
|
|
1087
|
+
const segment = pageItemsSegment;
|
|
1088
|
+
let serde;
|
|
1089
|
+
switch (format) {
|
|
1090
|
+
case 'json':
|
|
1091
|
+
serde = segment.serializationOptions.json.name;
|
|
1092
|
+
break;
|
|
1093
|
+
case 'xml':
|
|
1094
|
+
serde = segment.serializationOptions.xml.name;
|
|
1095
|
+
break;
|
|
1096
|
+
}
|
|
1097
|
+
for (let i = 0; i < typeToUnwrap.fields.length; ++i) {
|
|
1098
|
+
const field = typeToUnwrap.fields[i];
|
|
1099
|
+
if (field.serde === serde) {
|
|
1100
|
+
// check if this has already been unwrapped (e.g. type is shared across operations)
|
|
1101
|
+
if (field.type.kind === 'option') {
|
|
1102
|
+
typeToUnwrap.fields[i].type = typeToUnwrap.fields[i].type.type;
|
|
1103
|
+
}
|
|
1104
|
+
// move to the next segment
|
|
1105
|
+
if (field.type.kind === 'model') {
|
|
1106
|
+
typeToUnwrap = field.type;
|
|
1107
|
+
}
|
|
1108
|
+
++unwrappedCount;
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
if (unwrappedCount !== method.pagingMetadata.pageItemsSegments.length) {
|
|
1114
|
+
throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (_j = method.__raw) === null || _j === void 0 ? void 0 : _j.node);
|
|
1115
|
+
}
|
|
993
1116
|
returnType = new rust.Pager(this.crate, new rust.Payload(synthesizedModel, format));
|
|
994
1117
|
}
|
|
995
1118
|
else if (method.response.type && !(method.response.type.kind === 'bytes' && method.response.type.encode === 'bytes')) {
|
|
@@ -1011,14 +1134,42 @@ export class Adapter {
|
|
|
1011
1134
|
returnType = new rust.Response(this.crate, this.getUnitType());
|
|
1012
1135
|
}
|
|
1013
1136
|
rustMethod.returns = new rust.Result(this.crate, returnType);
|
|
1014
|
-
|
|
1137
|
+
const responseHeadersMap = this.adaptResposeHeaders(responseHeaders);
|
|
1138
|
+
rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, Array.from(responseHeadersMap.values()));
|
|
1015
1139
|
if (method.kind === 'paging') {
|
|
1016
1140
|
// can't do this until the method has been completely adapted
|
|
1017
|
-
rustMethod.strategy = this.adaptPageableMethodStrategy(method);
|
|
1141
|
+
rustMethod.strategy = this.adaptPageableMethodStrategy(method, paramsMap, responseHeadersMap);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* adapts response headers into Rust response headers and provides
|
|
1146
|
+
* a mapping from the tcgc response header to the Rust equivalent.
|
|
1147
|
+
* if there are no headers to adapt, an empty map is returned.
|
|
1148
|
+
*
|
|
1149
|
+
* @param responseHeaders the response headers to adapt (can be empty)
|
|
1150
|
+
* @returns the map of response headers
|
|
1151
|
+
*/
|
|
1152
|
+
adaptResposeHeaders(responseHeaders) {
|
|
1153
|
+
const responseHeadersMap = new Map();
|
|
1154
|
+
// adapt the response headers and add them to the trait
|
|
1155
|
+
for (const header of responseHeaders) {
|
|
1156
|
+
let responseHeader;
|
|
1157
|
+
if (header.type.kind === 'dict') {
|
|
1158
|
+
if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
|
|
1159
|
+
throw new AdapterError('InternalError', `unexpected response header collection ${header.serializedName}`, header.__raw.node);
|
|
1160
|
+
}
|
|
1161
|
+
responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.typeToWireType(this.getType(header.type)));
|
|
1165
|
+
}
|
|
1166
|
+
responseHeader.docs = this.adaptDocs(header.summary, header.doc);
|
|
1167
|
+
responseHeadersMap.set(header, responseHeader);
|
|
1018
1168
|
}
|
|
1169
|
+
return responseHeadersMap;
|
|
1019
1170
|
}
|
|
1020
1171
|
/**
|
|
1021
|
-
*
|
|
1172
|
+
* creates a Rust ResponseHeadersTrait for the specified response headers.
|
|
1022
1173
|
* if there are no response headers, undefined is returned.
|
|
1023
1174
|
*
|
|
1024
1175
|
* @param client the client that contains the method
|
|
@@ -1030,6 +1181,32 @@ export class Adapter {
|
|
|
1030
1181
|
if (responseHeaders.length === 0) {
|
|
1031
1182
|
return undefined;
|
|
1032
1183
|
}
|
|
1184
|
+
/**
|
|
1185
|
+
* recursively builds a name from the specified type.
|
|
1186
|
+
* e.g. Vec<FooModel> would be VecFooModel etc.
|
|
1187
|
+
*
|
|
1188
|
+
* @param type the type for which to build a name
|
|
1189
|
+
* @returns the name
|
|
1190
|
+
*/
|
|
1191
|
+
const recursiveTypeName = function (type) {
|
|
1192
|
+
switch (type.kind) {
|
|
1193
|
+
case 'enum':
|
|
1194
|
+
case 'model':
|
|
1195
|
+
return type.name;
|
|
1196
|
+
case 'hashmap':
|
|
1197
|
+
return `${type.name}${recursiveTypeName(type.type)}`;
|
|
1198
|
+
case 'ref':
|
|
1199
|
+
return `Ref${recursiveTypeName(type.type)}`;
|
|
1200
|
+
case 'scalar':
|
|
1201
|
+
return codegen.capitalize(type.type);
|
|
1202
|
+
case 'slice':
|
|
1203
|
+
return `Slice${recursiveTypeName(type.type)}`;
|
|
1204
|
+
case 'Vec':
|
|
1205
|
+
return `${type.kind}${recursiveTypeName(type.type)}`;
|
|
1206
|
+
default:
|
|
1207
|
+
return codegen.capitalize(type.kind);
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1033
1210
|
// response header traits are only ever for marker types and payloads
|
|
1034
1211
|
let implFor;
|
|
1035
1212
|
switch (method.returns.type.kind) {
|
|
@@ -1038,13 +1215,13 @@ export class Adapter {
|
|
|
1038
1215
|
break;
|
|
1039
1216
|
case 'response':
|
|
1040
1217
|
if (method.returns.type.content.kind !== 'marker' && method.returns.type.content.kind !== 'payload') {
|
|
1041
|
-
throw new
|
|
1218
|
+
throw new AdapterError('InternalError', `unexpected method content kind ${method.returns.type.content.kind}`);
|
|
1042
1219
|
}
|
|
1043
1220
|
implFor = method.returns.type.content;
|
|
1044
1221
|
break;
|
|
1045
1222
|
default:
|
|
1046
1223
|
// this is Unit which should have been previously skipped
|
|
1047
|
-
throw new
|
|
1224
|
+
throw new AdapterError('InternalError', `unexpected method return kind ${method.returns.type.kind}`);
|
|
1048
1225
|
}
|
|
1049
1226
|
let traitName;
|
|
1050
1227
|
switch (implFor.kind) {
|
|
@@ -1052,83 +1229,99 @@ export class Adapter {
|
|
|
1052
1229
|
traitName = implFor.name;
|
|
1053
1230
|
break;
|
|
1054
1231
|
case 'payload':
|
|
1055
|
-
traitName =
|
|
1232
|
+
traitName = recursiveTypeName(implFor.type);
|
|
1056
1233
|
break;
|
|
1057
1234
|
}
|
|
1058
1235
|
traitName += 'Headers';
|
|
1059
1236
|
// NOTE: the complete doc text will be emitted at codegen time
|
|
1060
1237
|
const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
|
|
1061
1238
|
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs);
|
|
1062
|
-
|
|
1063
|
-
for (const header of responseHeaders) {
|
|
1064
|
-
let responseHeader;
|
|
1065
|
-
if (header.type.kind === 'dict') {
|
|
1066
|
-
if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
|
|
1067
|
-
throw new Error(`unexpected response header collection ${header.serializedName}`);
|
|
1068
|
-
}
|
|
1069
|
-
responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
|
|
1070
|
-
}
|
|
1071
|
-
else {
|
|
1072
|
-
responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.getType(header.type));
|
|
1073
|
-
}
|
|
1074
|
-
responseHeader.docs = this.adaptDocs(header.summary, header.doc);
|
|
1075
|
-
responseHeadersTrait.headers.push(responseHeader);
|
|
1076
|
-
}
|
|
1239
|
+
responseHeadersTrait.headers.push(...responseHeaders);
|
|
1077
1240
|
return responseHeadersTrait;
|
|
1078
1241
|
}
|
|
1079
|
-
/**
|
|
1080
|
-
* recursively builds a name from the specified type.
|
|
1081
|
-
* e.g. Vec<FooModel> would be VecFooModel etc.
|
|
1082
|
-
*
|
|
1083
|
-
* @param type the type for which to build a name
|
|
1084
|
-
* @returns the name
|
|
1085
|
-
*/
|
|
1086
|
-
recursiveTypeName(type) {
|
|
1087
|
-
switch (type.kind) {
|
|
1088
|
-
case 'enum':
|
|
1089
|
-
case 'model':
|
|
1090
|
-
return type.name;
|
|
1091
|
-
case 'hashmap':
|
|
1092
|
-
return `${type.name}${this.recursiveTypeName(type.type)}`;
|
|
1093
|
-
case 'Vec':
|
|
1094
|
-
return `${type.kind}${this.recursiveTypeName(type.type)}`;
|
|
1095
|
-
default:
|
|
1096
|
-
return codegen.capitalize(type.kind);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
1242
|
/**
|
|
1100
1243
|
* creates the pageable strategy based on the method definition
|
|
1101
1244
|
*
|
|
1102
1245
|
* @param method the pageable method for which to create a strategy
|
|
1246
|
+
* @param paramsMap maps tcgc method params to Rust params (needed for continuation token strategy)
|
|
1247
|
+
* @param respHeadersMap maps tcgc response headers to Rust response headers (needed for continuation token strategy)
|
|
1103
1248
|
* @returns the pageable strategy
|
|
1104
1249
|
*/
|
|
1105
|
-
adaptPageableMethodStrategy(method) {
|
|
1250
|
+
adaptPageableMethodStrategy(method, paramsMap, respHeadersMap) {
|
|
1251
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1106
1252
|
if (method.pagingMetadata.nextLinkOperation) {
|
|
1107
1253
|
// TODO: https://github.com/Azure/autorest.rust/issues/103
|
|
1108
|
-
throw new
|
|
1254
|
+
throw new AdapterError('UnsupportedTsp', 'next page operation NYI', (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1109
1255
|
}
|
|
1110
1256
|
else if (method.pagingMetadata.nextLinkSegments) {
|
|
1111
1257
|
if (method.pagingMetadata.nextLinkSegments.length > 1) {
|
|
1112
1258
|
// TODO: https://github.com/Azure/autorest.rust/issues/102
|
|
1113
|
-
throw new
|
|
1259
|
+
throw new AdapterError('UnsupportedTsp', 'nested next link path NYI', (_b = method.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
1114
1260
|
}
|
|
1115
1261
|
// this is the field in the response type that contains the next link URL
|
|
1116
1262
|
const nextLinkSegment = method.pagingMetadata.nextLinkSegments[0];
|
|
1117
1263
|
if (nextLinkSegment.kind !== 'property') {
|
|
1118
|
-
throw new
|
|
1264
|
+
throw new AdapterError('InternalError', `unexpected kind ${nextLinkSegment.kind} for next link segment in operation ${method.name}`, (_c = method.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
1119
1265
|
}
|
|
1120
1266
|
const nextLinkField = this.fieldsMap.get(nextLinkSegment);
|
|
1121
1267
|
if (!nextLinkField) {
|
|
1122
1268
|
// the most likely explanation for this is lack of reference equality
|
|
1123
|
-
throw new
|
|
1269
|
+
throw new AdapterError('InternalError', `missing next link field name ${nextLinkSegment.name} for operation ${method.name}`, (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
1124
1270
|
}
|
|
1125
1271
|
return new rust.PageableStrategyNextLink(nextLinkField);
|
|
1126
1272
|
}
|
|
1127
1273
|
else if (method.pagingMetadata.continuationTokenParameterSegments && method.pagingMetadata.continuationTokenResponseSegments) {
|
|
1128
|
-
|
|
1274
|
+
if (method.pagingMetadata.continuationTokenParameterSegments.length > 1) {
|
|
1275
|
+
throw new AdapterError('UnsupportedTsp', `nested continuationTokenParameterSegments NYI`, (_e = method.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
1276
|
+
}
|
|
1277
|
+
if (method.pagingMetadata.continuationTokenResponseSegments.length > 1) {
|
|
1278
|
+
throw new AdapterError('UnsupportedTsp', `nested continuationTokenResponseSegments NYI`, (_f = method.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
1279
|
+
}
|
|
1280
|
+
const tokenReq = method.pagingMetadata.continuationTokenParameterSegments[0];
|
|
1281
|
+
const tokenResp = method.pagingMetadata.continuationTokenResponseSegments[0];
|
|
1282
|
+
// find the continuation token parameter
|
|
1283
|
+
let requestToken;
|
|
1284
|
+
switch (tokenReq.kind) {
|
|
1285
|
+
case 'method': {
|
|
1286
|
+
const tokenParam = paramsMap.get(tokenReq);
|
|
1287
|
+
if (!tokenParam) {
|
|
1288
|
+
throw new AdapterError('InternalError', `missing continuation token request parameter name ${tokenResp.name} for operation ${method.name}`, (_g = method.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
1289
|
+
}
|
|
1290
|
+
requestToken = tokenParam;
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
default:
|
|
1294
|
+
throw new AdapterError('InternalError', `unhandled continuationTokenParameterSegment kind ${tokenReq.kind}`, (_h = tokenReq.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
1295
|
+
}
|
|
1296
|
+
// find the continuation token response
|
|
1297
|
+
let responseToken;
|
|
1298
|
+
switch (tokenResp.kind) {
|
|
1299
|
+
case 'property': {
|
|
1300
|
+
const tokenField = this.fieldsMap.get(tokenResp);
|
|
1301
|
+
if (!tokenField) {
|
|
1302
|
+
throw new AdapterError('InternalError', `missing continuation token response field name ${tokenResp.name} for operation ${method.name}`, (_j = method.__raw) === null || _j === void 0 ? void 0 : _j.node);
|
|
1303
|
+
}
|
|
1304
|
+
responseToken = tokenField;
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
case 'responseheader': {
|
|
1308
|
+
const tokenHeader = respHeadersMap.get(tokenResp);
|
|
1309
|
+
if (!tokenHeader) {
|
|
1310
|
+
throw new AdapterError('InternalError', `missing continuation token response header name ${tokenResp.name} for operation ${method.name}`, (_k = method.__raw) === null || _k === void 0 ? void 0 : _k.node);
|
|
1311
|
+
}
|
|
1312
|
+
if (tokenHeader.kind !== 'responseHeaderScalar') {
|
|
1313
|
+
throw new AdapterError('InternalError', `unexpected response header kind ${tokenHeader.kind}`);
|
|
1314
|
+
}
|
|
1315
|
+
responseToken = tokenHeader;
|
|
1316
|
+
break;
|
|
1317
|
+
}
|
|
1318
|
+
default:
|
|
1319
|
+
throw new AdapterError('InternalError', `unhandled continuationTokenResponseSegment kind ${tokenResp.kind}`);
|
|
1320
|
+
}
|
|
1321
|
+
return new rust.PageableStrategyContinuationToken(requestToken, responseToken);
|
|
1129
1322
|
}
|
|
1130
1323
|
else {
|
|
1131
|
-
throw new
|
|
1324
|
+
throw new AdapterError('InternalError', `unknown paging strategy for operation ${method.name}`, (_l = method.__raw) === null || _l === void 0 ? void 0 : _l.node);
|
|
1132
1325
|
}
|
|
1133
1326
|
}
|
|
1134
1327
|
/**
|
|
@@ -1138,6 +1331,7 @@ export class Adapter {
|
|
|
1138
1331
|
* @returns a Rust method parameter
|
|
1139
1332
|
*/
|
|
1140
1333
|
adaptMethodParameter(param) {
|
|
1334
|
+
var _a, _b, _c, _d, _e;
|
|
1141
1335
|
const paramLoc = param.onClient ? 'client' : 'method';
|
|
1142
1336
|
/**
|
|
1143
1337
|
* used to create keys for this.clientMethodParams
|
|
@@ -1158,11 +1352,12 @@ export class Adapter {
|
|
|
1158
1352
|
}
|
|
1159
1353
|
const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
|
|
1160
1354
|
let paramType = this.getType(param.type);
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1355
|
+
// for required header/path/query method string params, we might emit them as borrowed types
|
|
1356
|
+
if (!param.optional && !param.onClient && (param.kind === 'header' || param.kind === 'path' || param.kind === 'query')) {
|
|
1357
|
+
const borrowedType = this.canBorrowMethodParam(paramType, param.kind);
|
|
1358
|
+
if (borrowedType) {
|
|
1359
|
+
paramType = borrowedType;
|
|
1360
|
+
}
|
|
1166
1361
|
}
|
|
1167
1362
|
let adaptedParam;
|
|
1168
1363
|
switch (param.kind) {
|
|
@@ -1180,11 +1375,11 @@ export class Adapter {
|
|
|
1180
1375
|
}
|
|
1181
1376
|
case 'cookie':
|
|
1182
1377
|
// TODO: https://github.com/Azure/typespec-rust/issues/192
|
|
1183
|
-
throw new
|
|
1378
|
+
throw new AdapterError('UnsupportedTsp', 'cookie parameters are not supported', (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1184
1379
|
case 'header':
|
|
1185
1380
|
if (param.collectionFormat) {
|
|
1186
|
-
if (paramType.kind !== 'Vec') {
|
|
1187
|
-
throw new
|
|
1381
|
+
if (paramType.kind !== 'Vec' && !isRefSlice(paramType)) {
|
|
1382
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for HeaderCollectionParameter`, (_b = param.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
1188
1383
|
}
|
|
1189
1384
|
let format;
|
|
1190
1385
|
switch (param.collectionFormat) {
|
|
@@ -1198,47 +1393,91 @@ export class Adapter {
|
|
|
1198
1393
|
format = param.collectionFormat;
|
|
1199
1394
|
break;
|
|
1200
1395
|
default:
|
|
1201
|
-
throw new
|
|
1396
|
+
throw new AdapterError('InternalError', `unexpected format ${param.collectionFormat} for HeaderCollectionParameter`, (_c = param.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
1202
1397
|
}
|
|
1203
1398
|
adaptedParam = new rust.HeaderCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, format);
|
|
1204
1399
|
}
|
|
1205
1400
|
else if (param.serializedName === 'x-ms-meta') {
|
|
1206
1401
|
if (paramType.kind !== 'hashmap') {
|
|
1207
|
-
throw new
|
|
1402
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for header ${param.serializedName}`, (_d = param.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
1208
1403
|
}
|
|
1209
1404
|
adaptedParam = new rust.HeaderHashMapParameter(paramName, param.serializedName, paramLoc, param.optional, paramType);
|
|
1210
1405
|
}
|
|
1211
1406
|
else {
|
|
1212
|
-
adaptedParam = new rust.HeaderParameter(paramName, param.serializedName, paramLoc, param.optional, paramType);
|
|
1407
|
+
adaptedParam = new rust.HeaderParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType));
|
|
1213
1408
|
adaptedParam.isApiVersion = param.isApiVersionParam;
|
|
1214
1409
|
}
|
|
1215
1410
|
break;
|
|
1216
1411
|
case 'path':
|
|
1217
|
-
adaptedParam = new rust.PathParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, param.allowReserved);
|
|
1412
|
+
adaptedParam = new rust.PathParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType), param.allowReserved);
|
|
1218
1413
|
break;
|
|
1219
1414
|
case 'query':
|
|
1220
1415
|
if (param.collectionFormat) {
|
|
1221
1416
|
const format = param.collectionFormat === 'simple' ? 'csv' : (param.collectionFormat === 'form' ? 'multi' : param.collectionFormat);
|
|
1222
|
-
if (paramType.kind !== 'Vec') {
|
|
1223
|
-
throw new
|
|
1417
|
+
if (paramType.kind !== 'Vec' && !isRefSlice(paramType)) {
|
|
1418
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for QueryCollectionParameter`, (_e = param.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
1224
1419
|
}
|
|
1225
1420
|
// TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
|
|
1226
1421
|
adaptedParam = new rust.QueryCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, true, format);
|
|
1227
1422
|
}
|
|
1228
1423
|
else {
|
|
1229
1424
|
// TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
|
|
1230
|
-
adaptedParam = new rust.QueryParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, true);
|
|
1425
|
+
adaptedParam = new rust.QueryParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType), true);
|
|
1231
1426
|
adaptedParam.isApiVersion = param.isApiVersionParam;
|
|
1232
1427
|
}
|
|
1233
1428
|
break;
|
|
1234
1429
|
}
|
|
1235
1430
|
adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
|
|
1236
|
-
adaptedParam.ref = paramByRef;
|
|
1237
1431
|
if (paramLoc === 'client') {
|
|
1238
1432
|
this.clientMethodParams.set(getClientParamsKey(param), adaptedParam);
|
|
1239
1433
|
}
|
|
1240
1434
|
return adaptedParam;
|
|
1241
1435
|
}
|
|
1436
|
+
/**
|
|
1437
|
+
* updates the specified type to a borrowed type based on its type and kind.
|
|
1438
|
+
* if no such transformation is necessary, undefined is returned.
|
|
1439
|
+
* e.g. a String param that doesn't need to be owned will be
|
|
1440
|
+
* returned as a &str.
|
|
1441
|
+
*
|
|
1442
|
+
* @param type the param type to be updated
|
|
1443
|
+
* @param kind the kind of param
|
|
1444
|
+
* @returns the updated param type or undefined
|
|
1445
|
+
*/
|
|
1446
|
+
canBorrowMethodParam(type, kind) {
|
|
1447
|
+
const recursiveBuildVecStr = (v) => {
|
|
1448
|
+
switch (v.kind) {
|
|
1449
|
+
case 'encodedBytes':
|
|
1450
|
+
return this.getRefType(this.getEncodedBytes(v.encoding, true));
|
|
1451
|
+
case 'hashmap':
|
|
1452
|
+
return this.getHashMap(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
1453
|
+
case 'String':
|
|
1454
|
+
return this.getRefType(this.getStringSlice());
|
|
1455
|
+
case 'Vec':
|
|
1456
|
+
return this.getVec(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
1457
|
+
default:
|
|
1458
|
+
throw new AdapterError('InternalError', `unexpected kind ${v.kind}`);
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
switch (type.kind) {
|
|
1462
|
+
case 'String':
|
|
1463
|
+
// header String params are always owned
|
|
1464
|
+
if (kind !== 'header') {
|
|
1465
|
+
return this.getRefType(this.getStringSlice());
|
|
1466
|
+
}
|
|
1467
|
+
break;
|
|
1468
|
+
case 'Vec': {
|
|
1469
|
+
// if this is an array of string, we ultimately want a slice of &str
|
|
1470
|
+
const unwrapped = helpers.unwrapVec(type);
|
|
1471
|
+
if (unwrapped.kind === 'String' || unwrapped.kind === 'encodedBytes') {
|
|
1472
|
+
return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)));
|
|
1473
|
+
}
|
|
1474
|
+
return this.getRefType(this.getSlice(type.type));
|
|
1475
|
+
}
|
|
1476
|
+
case 'encodedBytes':
|
|
1477
|
+
return this.getRefType(this.getEncodedBytes(type.encoding, true));
|
|
1478
|
+
}
|
|
1479
|
+
return undefined;
|
|
1480
|
+
}
|
|
1242
1481
|
/**
|
|
1243
1482
|
* narrows a rust.Type to a rust.WireType.
|
|
1244
1483
|
* if type isn't a rust.WireType, an error is thrown.
|
|
@@ -1249,6 +1488,7 @@ export class Adapter {
|
|
|
1249
1488
|
typeToWireType(type) {
|
|
1250
1489
|
switch (type.kind) {
|
|
1251
1490
|
case 'bytes':
|
|
1491
|
+
case 'decimal':
|
|
1252
1492
|
case 'encodedBytes':
|
|
1253
1493
|
case 'enum':
|
|
1254
1494
|
case 'enumValue':
|
|
@@ -1258,14 +1498,16 @@ export class Adapter {
|
|
|
1258
1498
|
case 'literal':
|
|
1259
1499
|
case 'model':
|
|
1260
1500
|
case 'offsetDateTime':
|
|
1501
|
+
case 'ref':
|
|
1261
1502
|
case 'scalar':
|
|
1503
|
+
case 'slice':
|
|
1262
1504
|
case 'str':
|
|
1263
1505
|
case 'String':
|
|
1264
1506
|
case 'Url':
|
|
1265
1507
|
case 'Vec':
|
|
1266
1508
|
return type;
|
|
1267
1509
|
default:
|
|
1268
|
-
throw new
|
|
1510
|
+
throw new AdapterError('InternalError', `cannot convert ${type.kind} to a wire type`);
|
|
1269
1511
|
}
|
|
1270
1512
|
}
|
|
1271
1513
|
/**
|
|
@@ -1277,6 +1519,7 @@ export class Adapter {
|
|
|
1277
1519
|
* @returns a Rust partial body parameter
|
|
1278
1520
|
*/
|
|
1279
1521
|
adaptMethodSpreadParameter(param, format, opParamType) {
|
|
1522
|
+
var _a, _b;
|
|
1280
1523
|
switch (format) {
|
|
1281
1524
|
case 'json':
|
|
1282
1525
|
case 'xml': {
|
|
@@ -1289,27 +1532,16 @@ export class Adapter {
|
|
|
1289
1532
|
}
|
|
1290
1533
|
}
|
|
1291
1534
|
if (serializedName === undefined) {
|
|
1292
|
-
throw new
|
|
1293
|
-
}
|
|
1294
|
-
// this is the param type as surfaced in the method sig.
|
|
1295
|
-
// it's usually the same type as its corresponding field type
|
|
1296
|
-
// in the underlying model. the only exception is for String
|
|
1297
|
-
// types. we want to surface those as &str in the method sig.
|
|
1298
|
-
let paramType = this.getType(param.type);
|
|
1299
|
-
let paramByRef = false;
|
|
1300
|
-
if (!param.optional && !param.onClient && paramType.kind === 'String') {
|
|
1301
|
-
paramType = this.getStringSlice();
|
|
1302
|
-
paramByRef = true;
|
|
1535
|
+
throw new AdapterError('InternalError', `didn't find body model property for spread parameter ${param.name}`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1303
1536
|
}
|
|
1304
1537
|
// this is the internal model type that the spread params coalesce into
|
|
1305
1538
|
const payloadType = this.getType(opParamType);
|
|
1306
1539
|
if (payloadType.kind !== 'model') {
|
|
1307
|
-
throw new
|
|
1540
|
+
throw new AdapterError('InternalError', `unexpected kind ${payloadType.kind} for spread body param`, (_b = opParamType.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
1308
1541
|
}
|
|
1309
1542
|
const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
|
|
1310
1543
|
const paramLoc = 'method';
|
|
1311
|
-
const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName,
|
|
1312
|
-
adaptedParam.ref = paramByRef;
|
|
1544
|
+
const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, new rust.Payload(payloadType, format)));
|
|
1313
1545
|
return adaptedParam;
|
|
1314
1546
|
}
|
|
1315
1547
|
}
|
|
@@ -1332,10 +1564,14 @@ export class Adapter {
|
|
|
1332
1564
|
return 'xml';
|
|
1333
1565
|
}
|
|
1334
1566
|
else {
|
|
1335
|
-
throw new
|
|
1567
|
+
throw new AdapterError('InternalError', `unexpected contentType ${contentType}`);
|
|
1336
1568
|
}
|
|
1337
1569
|
}
|
|
1338
1570
|
}
|
|
1571
|
+
/** typeguard to determine if type is a Ref<Slice> */
|
|
1572
|
+
function isRefSlice(type) {
|
|
1573
|
+
return type.kind === 'ref' && type.type.kind === 'slice';
|
|
1574
|
+
}
|
|
1339
1575
|
/**
|
|
1340
1576
|
* transforms Etag etc to all lower case.
|
|
1341
1577
|
* this is to prevent inadvertently snake-casing
|
|
@@ -1366,41 +1602,36 @@ function snakeCaseName(name) {
|
|
|
1366
1602
|
*
|
|
1367
1603
|
* obj is recursively unwrapped, and each layer is used to construct
|
|
1368
1604
|
* the key. e.g. if obj is a HashMap<String, Vec<i32>> this would
|
|
1369
|
-
* unwrap to
|
|
1605
|
+
* unwrap to hashmap-Vec-i32.
|
|
1370
1606
|
*
|
|
1371
1607
|
* @param root the starting value for the key
|
|
1372
1608
|
* @param obj the type for which to create the key
|
|
1373
1609
|
* @returns a string containing the complete map key
|
|
1374
1610
|
*/
|
|
1375
|
-
function recursiveKeyName(root,
|
|
1376
|
-
switch (
|
|
1377
|
-
case '
|
|
1378
|
-
return recursiveKeyName(`${root}
|
|
1379
|
-
case '
|
|
1380
|
-
return `${root}
|
|
1611
|
+
function recursiveKeyName(root, type) {
|
|
1612
|
+
switch (type.kind) {
|
|
1613
|
+
case 'Vec':
|
|
1614
|
+
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
1615
|
+
case 'encodedBytes':
|
|
1616
|
+
return `${root}-${type.kind}-${type.encoding}${type.slice ? '-slice' : ''}`;
|
|
1381
1617
|
case 'enum':
|
|
1382
|
-
return `${root}-${
|
|
1383
|
-
case '
|
|
1384
|
-
return `${root}-${
|
|
1385
|
-
case '
|
|
1386
|
-
return recursiveKeyName(`${root}
|
|
1387
|
-
case '
|
|
1388
|
-
return `${root}-
|
|
1389
|
-
case 'utcDateTime':
|
|
1390
|
-
return `${root}-${obj.encode}`;
|
|
1391
|
-
case 'duration':
|
|
1392
|
-
// TODO: this should be: return `${root}-duration-${obj.encode}`;
|
|
1393
|
-
// as it is now, it treats the duration as a String
|
|
1394
|
-
// https://github.com/Azure/typespec-rust/issues/41
|
|
1395
|
-
return `${root}-${obj.wireType.kind}`;
|
|
1618
|
+
return `${root}-${type.kind}-${type.name}`;
|
|
1619
|
+
case 'enumValue':
|
|
1620
|
+
return `${root}-${type.type.name}-${type.name}`;
|
|
1621
|
+
case 'hashmap':
|
|
1622
|
+
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
1623
|
+
case 'offsetDateTime':
|
|
1624
|
+
return `${root}-${type.kind}-${type.encoding}${type.utc ? '-utc' : ''}`;
|
|
1396
1625
|
case 'model':
|
|
1397
|
-
return `${root}-${
|
|
1398
|
-
case '
|
|
1399
|
-
return recursiveKeyName(root
|
|
1400
|
-
case '
|
|
1401
|
-
return `${root}
|
|
1626
|
+
return `${root}-${type.kind}-${type.name}`;
|
|
1627
|
+
case 'ref':
|
|
1628
|
+
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
1629
|
+
case 'scalar':
|
|
1630
|
+
return `${root}-${type.kind}-${type.type}`;
|
|
1631
|
+
case 'slice':
|
|
1632
|
+
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
1402
1633
|
default:
|
|
1403
|
-
return `${root}-${
|
|
1634
|
+
return `${root}-${type.kind}`;
|
|
1404
1635
|
}
|
|
1405
1636
|
}
|
|
1406
1637
|
/**
|