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