@azure-tools/typespec-rust 0.13.3 → 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.
Files changed (50) hide show
  1. package/dist/src/codegen/clients.d.ts.map +1 -1
  2. package/dist/src/codegen/clients.js +155 -85
  3. package/dist/src/codegen/clients.js.map +1 -1
  4. package/dist/src/codegen/context.d.ts.map +1 -1
  5. package/dist/src/codegen/context.js +3 -2
  6. package/dist/src/codegen/context.js.map +1 -1
  7. package/dist/src/codegen/errors.d.ts +13 -0
  8. package/dist/src/codegen/errors.d.ts.map +1 -0
  9. package/dist/src/codegen/errors.js +15 -0
  10. package/dist/src/codegen/errors.js.map +1 -0
  11. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  12. package/dist/src/codegen/headerTraits.js +3 -2
  13. package/dist/src/codegen/headerTraits.js.map +1 -1
  14. package/dist/src/codegen/helpers.d.ts.map +1 -1
  15. package/dist/src/codegen/helpers.js +13 -3
  16. package/dist/src/codegen/helpers.js.map +1 -1
  17. package/dist/src/codegen/models.d.ts.map +1 -1
  18. package/dist/src/codegen/models.js +133 -71
  19. package/dist/src/codegen/models.js.map +1 -1
  20. package/dist/src/codegen/use.d.ts.map +1 -1
  21. package/dist/src/codegen/use.js +4 -2
  22. package/dist/src/codegen/use.js.map +1 -1
  23. package/dist/src/codemodel/client.d.ts +17 -31
  24. package/dist/src/codemodel/client.d.ts.map +1 -1
  25. package/dist/src/codemodel/client.js +1 -35
  26. package/dist/src/codemodel/client.js.map +1 -1
  27. package/dist/src/codemodel/method.d.ts +0 -2
  28. package/dist/src/codemodel/method.d.ts.map +1 -1
  29. package/dist/src/codemodel/method.js +0 -1
  30. package/dist/src/codemodel/method.js.map +1 -1
  31. package/dist/src/codemodel/types.d.ts +52 -5
  32. package/dist/src/codemodel/types.d.ts.map +1 -1
  33. package/dist/src/codemodel/types.js +26 -7
  34. package/dist/src/codemodel/types.js.map +1 -1
  35. package/dist/src/emitter.d.ts.map +1 -1
  36. package/dist/src/emitter.js +84 -34
  37. package/dist/src/emitter.js.map +1 -1
  38. package/dist/src/lib.d.ts +37 -4
  39. package/dist/src/lib.d.ts.map +1 -1
  40. package/dist/src/lib.js +27 -2
  41. package/dist/src/lib.js.map +1 -1
  42. package/dist/src/tcgcadapter/adapter.d.ts +58 -10
  43. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  44. package/dist/src/tcgcadapter/adapter.js +390 -176
  45. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  46. package/dist/src/tcgcadapter/helpers.d.ts +8 -0
  47. package/dist/src/tcgcadapter/helpers.d.ts.map +1 -1
  48. package/dist/src/tcgcadapter/helpers.js +13 -0
  49. package/dist/src/tcgcadapter/helpers.js.map +1 -1
  50. 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 Error(`didn't find enum value for name ${sdkEnumValue.name} in enum ${enumType.name}`);
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 Error('unnamed model'); // TODO: this might no longer be an issue
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 Error(`model property kind ${property.kind} NYI`);
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
- // the only exception is for HashMap and Vec since an
224
- // empty collection conveys the same semantics.
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
- /** converts a tcgc type to a Rust type */
243
- getType(type) {
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
- scalar = new rust.Scalar(scalarType);
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
- const keyName = recursiveKeyName(type.kind, type.valueType);
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
- const keyName = `encodedBytes-${encoding}`;
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 'dict': {
327
- const keyName = recursiveKeyName(type.kind, type.valueType);
328
- let hashmapType = this.types.get(keyName);
329
- if (hashmapType) {
330
- return hashmapType;
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
- hashmapType = new rust.HashMap(this.typeToWireType(this.getType(type.valueType)));
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 Error(`unhandled duration wireType.kind ${type.wireType.kind}`);
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 = 'etag';
385
+ const etagKey = 'Etag';
372
386
  let etagType = this.types.get(etagKey);
373
387
  if (etagType) {
374
388
  return etagType;
@@ -381,40 +395,94 @@ export class Adapter {
381
395
  }
382
396
  case 'nullable':
383
397
  // TODO: workaround until https://github.com/Azure/typespec-rust/issues/42 is fixed
384
- return this.getType(type.type);
398
+ return this.getType(type.type, stack);
385
399
  case 'offsetDateTime': {
386
- const keyName = `${type.kind}-${type.encode}`;
400
+ const encoding = getDateTimeEncoding(type.encode);
401
+ const keyName = `offsetDateTime-${encoding}`;
387
402
  let timeType = this.types.get(keyName);
388
403
  if (timeType) {
389
404
  return timeType;
390
405
  }
391
- timeType = new rust.OffsetDateTime(this.crate, getDateTimeEncoding(type.encode), false);
406
+ timeType = new rust.OffsetDateTime(this.crate, encoding, false);
392
407
  this.types.set(keyName, timeType);
393
408
  return timeType;
394
409
  }
395
410
  case 'unknown': {
396
- let anyType = this.types.get(type.kind);
411
+ const keyName = 'jsonValue';
412
+ let anyType = this.types.get(keyName);
397
413
  if (anyType) {
398
414
  return anyType;
399
415
  }
400
416
  anyType = new rust.JsonValue(this.crate);
401
- this.types.set(type.kind, anyType);
417
+ this.types.set(keyName, anyType);
402
418
  return anyType;
403
419
  }
404
420
  case 'utcDateTime': {
405
- const keyName = `${type.kind}-${type.encode}`;
421
+ const encoding = getDateTimeEncoding(type.encode);
422
+ const keyName = `offsetDateTime-${encoding}-utc`;
406
423
  let timeType = this.types.get(keyName);
407
424
  if (timeType) {
408
425
  return timeType;
409
426
  }
410
- timeType = new rust.OffsetDateTime(this.crate, getDateTimeEncoding(type.encode), true);
427
+ timeType = new rust.OffsetDateTime(this.crate, encoding, true);
411
428
  this.types.set(keyName, timeType);
412
429
  return timeType;
413
430
  }
414
431
  default:
415
- throw new Error(`unhandled tcgc type ${type.kind}`);
432
+ throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (_b = type.__raw) === null || _b === void 0 ? void 0 : _b.node);
416
433
  }
417
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
+ }
418
486
  /** returns the Rust string slice type */
419
487
  getStringSlice() {
420
488
  const typeKey = 'str';
@@ -448,6 +516,17 @@ export class Adapter {
448
516
  this.types.set(typeKey, unitType);
449
517
  return unitType;
450
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
+ }
451
530
  /**
452
531
  * converts a tcgc constant to a Rust literal
453
532
  *
@@ -497,7 +576,7 @@ export class Adapter {
497
576
  * @returns a Rust client
498
577
  */
499
578
  recursiveAdaptClient(client, parent) {
500
- var _a;
579
+ var _a, _b, _c, _d, _e;
501
580
  let clientName = client.name;
502
581
  // NOTE: if the client has the @clientName decorator applied then use that verbatim
503
582
  if (parent && !client.decorators.find((decorator) => decorator.name === 'Azure.ClientGenerator.Core.@clientName')) {
@@ -564,7 +643,7 @@ export class Adapter {
564
643
  }
565
644
  default:
566
645
  if (throwOnDefault) {
567
- throw new Error(`credential scheme type ${cred.type} NYI`);
646
+ throw new AdapterError('UnsupportedTsp', `credential scheme type ${cred.type} NYI`);
568
647
  }
569
648
  return AuthTypes.Default;
570
649
  }
@@ -588,7 +667,7 @@ export class Adapter {
588
667
  }
589
668
  // no supported credential types were specified
590
669
  if (authType === AuthTypes.Default) {
591
- throw new Error(`credential scheme types ${variantKinds.join()} NYI`);
670
+ throw new AdapterError('UnsupportedTsp', `credential scheme types ${variantKinds.join()} NYI`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
592
671
  }
593
672
  continue;
594
673
  }
@@ -616,7 +695,7 @@ export class Adapter {
616
695
  // note that the types of the param and the field are different.
617
696
  // NOTE: we use param.name here instead of templateArg.name as
618
697
  // the former has the fixed name "endpoint" which is what we want.
619
- const adaptedParam = new rust.ClientMethodParameter(param.name, this.getStringSlice(), false, true);
698
+ const adaptedParam = new rust.ClientMethodParameter(param.name, this.getRefType(this.getStringSlice()), false);
620
699
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
621
700
  ctorParams.push(adaptedParam);
622
701
  rustClient.fields.push(new rust.StructField(param.name, 'pubCrate', new rust.Url(this.crate)));
@@ -645,9 +724,9 @@ export class Adapter {
645
724
  }
646
725
  const clientParam = this.adaptClientParameter(templateArg, rustClient.constructable);
647
726
  if (clientParam.kind !== 'clientEndpoint') {
648
- throw new Error(`unexpected client parameter kind ${clientParam.kind}`);
727
+ throw new AdapterError('InternalError', `unexpected client parameter kind ${clientParam.kind}`, (_b = templateArg.__raw) === null || _b === void 0 ? void 0 : _b.node);
649
728
  }
650
- (_a = rustClient.constructable.endpoint) === null || _a === void 0 ? void 0 : _a.parameters.push(clientParam);
729
+ (_c = rustClient.constructable.endpoint) === null || _c === void 0 ? void 0 : _c.parameters.push(clientParam);
651
730
  ctorParams.push(clientParam);
652
731
  }
653
732
  break;
@@ -694,7 +773,7 @@ export class Adapter {
694
773
  }
695
774
  }
696
775
  else {
697
- throw new Error(`uninstantiable client ${client.name} has no parent`);
776
+ throw new AdapterError('InternalError', `uninstantiable client ${client.name} has no parent`);
698
777
  }
699
778
  for (const child of values(client.children)) {
700
779
  const subClient = this.recursiveAdaptClient(child, rustClient);
@@ -708,7 +787,7 @@ export class Adapter {
708
787
  code: 'LroNotSupported',
709
788
  severity: 'warning',
710
789
  message: `skip emitting LRO ${method.name}`,
711
- target: method.__raw ? method.__raw.node : NoTarget,
790
+ target: (_e = (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : NoTarget,
712
791
  });
713
792
  continue;
714
793
  }
@@ -726,12 +805,15 @@ export class Adapter {
726
805
  */
727
806
  createTokenCredentialCtor(rustClient, cred) {
728
807
  if (cred.flows.length === 0) {
729
- throw new Error(`no flows defined for credential type ${cred.type}`);
808
+ throw new AdapterError('InternalError', `no flows defined for credential type ${cred.type}`, cred.model);
730
809
  }
731
810
  const scopes = new Array();
732
811
  for (const scope of cred.flows[0].scopes) {
733
812
  scopes.push(scope.value);
734
813
  }
814
+ if (scopes.length === 0) {
815
+ throw new AdapterError('InternalError', 'scopes must contain at least one entry', cred.model);
816
+ }
735
817
  const ctorTokenCredential = new rust.Constructor('new');
736
818
  const tokenCredParam = new rust.ClientMethodParameter('credential', new rust.Arc(new rust.TokenCredential(this.crate, scopes)), false);
737
819
  tokenCredParam.docs.summary = `An implementation of [\`TokenCredential\`](azure_core::credentials::TokenCredential) that can provide an Entra ID token to use when authenticating.`;
@@ -747,10 +829,11 @@ export class Adapter {
747
829
  * @returns the Rust client parameter
748
830
  */
749
831
  adaptClientParameter(param, constructable) {
832
+ var _a;
750
833
  let paramType;
751
834
  if (param.isApiVersionParam) {
752
835
  if (!param.clientDefaultValue) {
753
- throw new Error(`API version parameter ${param.name} has no clientDefaultValue`);
836
+ throw new AdapterError('InternalError', `API version parameter ${param.name} has no clientDefaultValue`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
754
837
  }
755
838
  paramType = this.getStringType();
756
839
  }
@@ -819,6 +902,7 @@ export class Adapter {
819
902
  * @param rustClient the client to which the method belongs
820
903
  */
821
904
  adaptMethod(method, rustClient) {
905
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
822
906
  let srcMethodName = method.name;
823
907
  if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
824
908
  const chunks = codegen.deconstruct(srcMethodName);
@@ -829,11 +913,11 @@ export class Adapter {
829
913
  code: 'PagingMethodRename',
830
914
  severity: 'warning',
831
915
  message: `renamed paging method from ${method.name} to ${srcMethodName}`,
832
- target: method.__raw ? method.__raw.node : NoTarget,
916
+ target: (_b = (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : NoTarget,
833
917
  });
834
918
  }
835
919
  else if (this.renamedMethods.has(srcMethodName)) {
836
- throw new Error(`method name ${srcMethodName} collides with a renamed method`);
920
+ throw new AdapterError('NameCollision', `method name ${srcMethodName} collides with a renamed method`, (_c = method.__raw) === null || _c === void 0 ? void 0 : _c.node);
837
921
  }
838
922
  const methodName = naming.getEscapedReservedName(snakeCaseName(srcMethodName), 'fn');
839
923
  const optionsLifetime = new rust.Lifetime('a');
@@ -868,7 +952,7 @@ export class Adapter {
868
952
  rustMethod = new rust.PageableMethod(methodName, rustClient, pub, methodOptions, httpMethod, httpPath);
869
953
  break;
870
954
  default:
871
- throw new Error(`method kind ${method.kind} NYI`);
955
+ throw new AdapterError('UnsupportedTsp', `method kind ${method.kind} NYI`, (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node);
872
956
  }
873
957
  rustMethod.docs = this.adaptDocs(method.summary, method.doc);
874
958
  rustClient.methods.push(rustMethod);
@@ -878,6 +962,8 @@ export class Adapter {
878
962
  if (method.operation.bodyParam) {
879
963
  allOpParams.push(method.operation.bodyParam);
880
964
  }
965
+ // maps tcgc method header/query params to their Rust method params
966
+ const paramsMap = new Map();
881
967
  for (const param of method.parameters) {
882
968
  // we need to translate from the method param to its underlying operation param.
883
969
  // most params have a one-to-one mapping. however, for spread params, there will
@@ -889,7 +975,7 @@ export class Adapter {
889
975
  }).any();
890
976
  }).first();
891
977
  if (!opParam) {
892
- throw new Error(`didn't find operation parameter for method ${method.name} parameter ${param.name}`);
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);
893
979
  }
894
980
  let adaptedParam;
895
981
  // for spread params there are two cases we need to consider.
@@ -903,6 +989,9 @@ export class Adapter {
903
989
  else {
904
990
  adaptedParam = this.adaptMethodParameter(opParam);
905
991
  }
992
+ if (adaptedParam.kind === 'header' || adaptedParam.kind === 'query') {
993
+ paramsMap.set(param, adaptedParam);
994
+ }
906
995
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
907
996
  rustMethod.params.push(adaptedParam);
908
997
  if (adaptedParam.optional) {
@@ -914,18 +1003,11 @@ export class Adapter {
914
1003
  // already wrapped in an Option<T> type.
915
1004
  const field = adaptedParam.type.content.type.fields.find(f => { return f.name === adaptedParam.name; });
916
1005
  if (!field) {
917
- throw new Error(`didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.type.name}`);
1006
+ throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.type.name}`);
918
1007
  }
919
1008
  fieldType = field.type;
920
1009
  }
921
1010
  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
1011
  fieldType = new rust.Option(adaptedParam.type);
930
1012
  }
931
1013
  const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
@@ -943,17 +1025,18 @@ export class Adapter {
943
1025
  }
944
1026
  }
945
1027
  const getBodyFormat = () => {
1028
+ var _a, _b;
946
1029
  // fetch the body format from the HTTP responses.
947
1030
  // they should all have the same type so no need to match responses to type.
948
1031
  let defaultContentType;
949
1032
  for (const httpResp of method.operation.responses) {
950
1033
  if (defaultContentType && httpResp.defaultContentType && defaultContentType !== httpResp.defaultContentType) {
951
- throw new Error(`method ${method.name} has conflicting content types`);
1034
+ throw new AdapterError('InternalError', `method ${method.name} has conflicting content types`, (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
952
1035
  }
953
1036
  defaultContentType = httpResp.defaultContentType;
954
1037
  }
955
1038
  if (!defaultContentType) {
956
- throw new Error(`unable to determine content type for method ${method.name}`);
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);
957
1040
  }
958
1041
  return this.adaptBodyFormat(defaultContentType);
959
1042
  };
@@ -983,16 +1066,53 @@ export class Adapter {
983
1066
  // however, we want the synthesized paged response envelope type instead.
984
1067
  const synthesizedType = method.operation.responses[0].type;
985
1068
  if (!synthesizedType) {
986
- throw new Error(`paged method ${method.name} has no synthesized response type`);
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);
987
1070
  }
988
1071
  else if (synthesizedType.kind !== 'model') {
989
- throw new Error(`paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`);
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);
990
1073
  }
991
1074
  const format = getBodyFormat();
992
- const synthesizedModel = this.getModel(synthesizedType);
1075
+ const synthesizedModel = this.getModel(synthesizedType, new Array());
993
1076
  if (!this.crate.models.includes(synthesizedModel)) {
994
1077
  this.crate.models.push(synthesizedModel);
995
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
+ }
996
1116
  returnType = new rust.Pager(this.crate, new rust.Payload(synthesizedModel, format));
997
1117
  }
998
1118
  else if (method.response.type && !(method.response.type.kind === 'bytes' && method.response.type.encode === 'bytes')) {
@@ -1018,7 +1138,7 @@ export class Adapter {
1018
1138
  rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, Array.from(responseHeadersMap.values()));
1019
1139
  if (method.kind === 'paging') {
1020
1140
  // can't do this until the method has been completely adapted
1021
- rustMethod.strategy = this.adaptPageableMethodStrategy(method);
1141
+ rustMethod.strategy = this.adaptPageableMethodStrategy(method, paramsMap, responseHeadersMap);
1022
1142
  }
1023
1143
  }
1024
1144
  /**
@@ -1036,12 +1156,12 @@ export class Adapter {
1036
1156
  let responseHeader;
1037
1157
  if (header.type.kind === 'dict') {
1038
1158
  if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
1039
- throw new Error(`unexpected response header collection ${header.serializedName}`);
1159
+ throw new AdapterError('InternalError', `unexpected response header collection ${header.serializedName}`, header.__raw.node);
1040
1160
  }
1041
1161
  responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
1042
1162
  }
1043
1163
  else {
1044
- responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.getType(header.type));
1164
+ responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.typeToWireType(this.getType(header.type)));
1045
1165
  }
1046
1166
  responseHeader.docs = this.adaptDocs(header.summary, header.doc);
1047
1167
  responseHeadersMap.set(header, responseHeader);
@@ -1061,6 +1181,32 @@ export class Adapter {
1061
1181
  if (responseHeaders.length === 0) {
1062
1182
  return undefined;
1063
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
+ };
1064
1210
  // response header traits are only ever for marker types and payloads
1065
1211
  let implFor;
1066
1212
  switch (method.returns.type.kind) {
@@ -1069,13 +1215,13 @@ export class Adapter {
1069
1215
  break;
1070
1216
  case 'response':
1071
1217
  if (method.returns.type.content.kind !== 'marker' && method.returns.type.content.kind !== 'payload') {
1072
- throw new Error(`unexpected method content kind ${method.returns.type.content.kind}`);
1218
+ throw new AdapterError('InternalError', `unexpected method content kind ${method.returns.type.content.kind}`);
1073
1219
  }
1074
1220
  implFor = method.returns.type.content;
1075
1221
  break;
1076
1222
  default:
1077
1223
  // this is Unit which should have been previously skipped
1078
- throw new Error(`unexpected method return kind ${method.returns.type.kind}`);
1224
+ throw new AdapterError('InternalError', `unexpected method return kind ${method.returns.type.kind}`);
1079
1225
  }
1080
1226
  let traitName;
1081
1227
  switch (implFor.kind) {
@@ -1083,7 +1229,7 @@ export class Adapter {
1083
1229
  traitName = implFor.name;
1084
1230
  break;
1085
1231
  case 'payload':
1086
- traitName = this.recursiveTypeName(implFor.type);
1232
+ traitName = recursiveTypeName(implFor.type);
1087
1233
  break;
1088
1234
  }
1089
1235
  traitName += 'Headers';
@@ -1093,59 +1239,89 @@ export class Adapter {
1093
1239
  responseHeadersTrait.headers.push(...responseHeaders);
1094
1240
  return responseHeadersTrait;
1095
1241
  }
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
1242
  /**
1117
1243
  * creates the pageable strategy based on the method definition
1118
1244
  *
1119
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)
1120
1248
  * @returns the pageable strategy
1121
1249
  */
1122
- adaptPageableMethodStrategy(method) {
1250
+ adaptPageableMethodStrategy(method, paramsMap, respHeadersMap) {
1251
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1123
1252
  if (method.pagingMetadata.nextLinkOperation) {
1124
1253
  // TODO: https://github.com/Azure/autorest.rust/issues/103
1125
- throw new Error('next page operation NYI');
1254
+ throw new AdapterError('UnsupportedTsp', 'next page operation NYI', (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
1126
1255
  }
1127
1256
  else if (method.pagingMetadata.nextLinkSegments) {
1128
1257
  if (method.pagingMetadata.nextLinkSegments.length > 1) {
1129
1258
  // TODO: https://github.com/Azure/autorest.rust/issues/102
1130
- throw new Error('nested next link path NYI');
1259
+ throw new AdapterError('UnsupportedTsp', 'nested next link path NYI', (_b = method.__raw) === null || _b === void 0 ? void 0 : _b.node);
1131
1260
  }
1132
1261
  // this is the field in the response type that contains the next link URL
1133
1262
  const nextLinkSegment = method.pagingMetadata.nextLinkSegments[0];
1134
1263
  if (nextLinkSegment.kind !== 'property') {
1135
- throw new Error(`unexpected kind ${nextLinkSegment.kind} for next link segment in operation ${method.name}`);
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);
1136
1265
  }
1137
1266
  const nextLinkField = this.fieldsMap.get(nextLinkSegment);
1138
1267
  if (!nextLinkField) {
1139
1268
  // the most likely explanation for this is lack of reference equality
1140
- throw new Error(`missing next link field name ${nextLinkSegment.name} for operation ${method.name}`);
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);
1141
1270
  }
1142
1271
  return new rust.PageableStrategyNextLink(nextLinkField);
1143
1272
  }
1144
1273
  else if (method.pagingMetadata.continuationTokenParameterSegments && method.pagingMetadata.continuationTokenResponseSegments) {
1145
- throw new Error('PageableStrategyContinuationToken NYI');
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);
1146
1322
  }
1147
1323
  else {
1148
- throw new Error(`unhandled paging strategy for operation ${method.name}`);
1324
+ throw new AdapterError('InternalError', `unknown paging strategy for operation ${method.name}`, (_l = method.__raw) === null || _l === void 0 ? void 0 : _l.node);
1149
1325
  }
1150
1326
  }
1151
1327
  /**
@@ -1155,6 +1331,7 @@ export class Adapter {
1155
1331
  * @returns a Rust method parameter
1156
1332
  */
1157
1333
  adaptMethodParameter(param) {
1334
+ var _a, _b, _c, _d, _e;
1158
1335
  const paramLoc = param.onClient ? 'client' : 'method';
1159
1336
  /**
1160
1337
  * used to create keys for this.clientMethodParams
@@ -1175,11 +1352,12 @@ export class Adapter {
1175
1352
  }
1176
1353
  const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
1177
1354
  let paramType = this.getType(param.type);
1178
- let paramByRef = false;
1179
- // for required header/path/query method string params, we emit them as &str instead of String
1180
- if (!param.optional && !param.onClient && paramType.kind === 'String' && (param.kind === 'header' || param.kind === 'path' || param.kind === 'query')) {
1181
- paramType = this.getStringSlice();
1182
- paramByRef = true;
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
+ }
1183
1361
  }
1184
1362
  let adaptedParam;
1185
1363
  switch (param.kind) {
@@ -1197,11 +1375,11 @@ export class Adapter {
1197
1375
  }
1198
1376
  case 'cookie':
1199
1377
  // TODO: https://github.com/Azure/typespec-rust/issues/192
1200
- throw new Error('cookie parameters are not supported');
1378
+ throw new AdapterError('UnsupportedTsp', 'cookie parameters are not supported', (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
1201
1379
  case 'header':
1202
1380
  if (param.collectionFormat) {
1203
- if (paramType.kind !== 'Vec') {
1204
- throw new Error(`unexpected kind ${paramType.kind} for HeaderCollectionParameter`);
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);
1205
1383
  }
1206
1384
  let format;
1207
1385
  switch (param.collectionFormat) {
@@ -1215,47 +1393,91 @@ export class Adapter {
1215
1393
  format = param.collectionFormat;
1216
1394
  break;
1217
1395
  default:
1218
- throw new Error(`unexpected format ${param.collectionFormat} for HeaderCollectionParameter`);
1396
+ throw new AdapterError('InternalError', `unexpected format ${param.collectionFormat} for HeaderCollectionParameter`, (_c = param.__raw) === null || _c === void 0 ? void 0 : _c.node);
1219
1397
  }
1220
1398
  adaptedParam = new rust.HeaderCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, format);
1221
1399
  }
1222
1400
  else if (param.serializedName === 'x-ms-meta') {
1223
1401
  if (paramType.kind !== 'hashmap') {
1224
- throw new Error(`unexpected kind ${paramType.kind} for header ${param.serializedName}`);
1402
+ throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for header ${param.serializedName}`, (_d = param.__raw) === null || _d === void 0 ? void 0 : _d.node);
1225
1403
  }
1226
1404
  adaptedParam = new rust.HeaderHashMapParameter(paramName, param.serializedName, paramLoc, param.optional, paramType);
1227
1405
  }
1228
1406
  else {
1229
- 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));
1230
1408
  adaptedParam.isApiVersion = param.isApiVersionParam;
1231
1409
  }
1232
1410
  break;
1233
1411
  case 'path':
1234
- 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);
1235
1413
  break;
1236
1414
  case 'query':
1237
1415
  if (param.collectionFormat) {
1238
1416
  const format = param.collectionFormat === 'simple' ? 'csv' : (param.collectionFormat === 'form' ? 'multi' : param.collectionFormat);
1239
- if (paramType.kind !== 'Vec') {
1240
- throw new Error(`unexpected kind ${paramType.kind} for QueryCollectionParameter`);
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);
1241
1419
  }
1242
1420
  // TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
1243
1421
  adaptedParam = new rust.QueryCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, true, format);
1244
1422
  }
1245
1423
  else {
1246
1424
  // 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);
1425
+ adaptedParam = new rust.QueryParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType), true);
1248
1426
  adaptedParam.isApiVersion = param.isApiVersionParam;
1249
1427
  }
1250
1428
  break;
1251
1429
  }
1252
1430
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
1253
- adaptedParam.ref = paramByRef;
1254
1431
  if (paramLoc === 'client') {
1255
1432
  this.clientMethodParams.set(getClientParamsKey(param), adaptedParam);
1256
1433
  }
1257
1434
  return adaptedParam;
1258
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
+ }
1259
1481
  /**
1260
1482
  * narrows a rust.Type to a rust.WireType.
1261
1483
  * if type isn't a rust.WireType, an error is thrown.
@@ -1266,6 +1488,7 @@ export class Adapter {
1266
1488
  typeToWireType(type) {
1267
1489
  switch (type.kind) {
1268
1490
  case 'bytes':
1491
+ case 'decimal':
1269
1492
  case 'encodedBytes':
1270
1493
  case 'enum':
1271
1494
  case 'enumValue':
@@ -1275,14 +1498,16 @@ export class Adapter {
1275
1498
  case 'literal':
1276
1499
  case 'model':
1277
1500
  case 'offsetDateTime':
1501
+ case 'ref':
1278
1502
  case 'scalar':
1503
+ case 'slice':
1279
1504
  case 'str':
1280
1505
  case 'String':
1281
1506
  case 'Url':
1282
1507
  case 'Vec':
1283
1508
  return type;
1284
1509
  default:
1285
- throw new Error(`cannot convert ${type.kind} to a wire type`);
1510
+ throw new AdapterError('InternalError', `cannot convert ${type.kind} to a wire type`);
1286
1511
  }
1287
1512
  }
1288
1513
  /**
@@ -1294,6 +1519,7 @@ export class Adapter {
1294
1519
  * @returns a Rust partial body parameter
1295
1520
  */
1296
1521
  adaptMethodSpreadParameter(param, format, opParamType) {
1522
+ var _a, _b;
1297
1523
  switch (format) {
1298
1524
  case 'json':
1299
1525
  case 'xml': {
@@ -1306,27 +1532,16 @@ export class Adapter {
1306
1532
  }
1307
1533
  }
1308
1534
  if (serializedName === undefined) {
1309
- throw new Error(`didn't find body model property for spread parameter ${param.name}`);
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;
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);
1320
1536
  }
1321
1537
  // this is the internal model type that the spread params coalesce into
1322
1538
  const payloadType = this.getType(opParamType);
1323
1539
  if (payloadType.kind !== 'model') {
1324
- throw new Error(`unexpected kind ${payloadType.kind} for spread body param`);
1540
+ throw new AdapterError('InternalError', `unexpected kind ${payloadType.kind} for spread body param`, (_b = opParamType.__raw) === null || _b === void 0 ? void 0 : _b.node);
1325
1541
  }
1326
1542
  const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
1327
1543
  const paramLoc = 'method';
1328
- const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, paramType, new rust.RequestContent(this.crate, new rust.Payload(payloadType, format)));
1329
- 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)));
1330
1545
  return adaptedParam;
1331
1546
  }
1332
1547
  }
@@ -1349,10 +1564,14 @@ export class Adapter {
1349
1564
  return 'xml';
1350
1565
  }
1351
1566
  else {
1352
- throw new Error(`unexpected contentType ${contentType}`);
1567
+ throw new AdapterError('InternalError', `unexpected contentType ${contentType}`);
1353
1568
  }
1354
1569
  }
1355
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
+ }
1356
1575
  /**
1357
1576
  * transforms Etag etc to all lower case.
1358
1577
  * this is to prevent inadvertently snake-casing
@@ -1383,41 +1602,36 @@ function snakeCaseName(name) {
1383
1602
  *
1384
1603
  * obj is recursively unwrapped, and each layer is used to construct
1385
1604
  * the key. e.g. if obj is a HashMap<String, Vec<i32>> this would
1386
- * unwrap to dict-array-int32.
1605
+ * unwrap to hashmap-Vec-i32.
1387
1606
  *
1388
1607
  * @param root the starting value for the key
1389
1608
  * @param obj the type for which to create the key
1390
1609
  * @returns a string containing the complete map key
1391
1610
  */
1392
- function recursiveKeyName(root, obj) {
1393
- switch (obj.kind) {
1394
- case 'array':
1395
- return recursiveKeyName(`${root}-array`, obj.valueType);
1396
- case 'bytes':
1397
- return `${root}-bytes-${obj.encode ? obj.encode : 'std'}`;
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' : ''}`;
1398
1617
  case 'enum':
1399
- return `${root}-${obj.name}`;
1400
- case 'enumvalue':
1401
- return `${root}-${obj.enumType.name}-${obj.value}`;
1402
- case 'dict':
1403
- return recursiveKeyName(`${root}-dict`, obj.valueType);
1404
- case 'plainDate':
1405
- return `${root}-plainDate`;
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}`;
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' : ''}`;
1413
1625
  case 'model':
1414
- return `${root}-${obj.name}`;
1415
- case 'nullable':
1416
- return recursiveKeyName(`${root}-nullable`, obj.type);
1417
- case 'plainTime':
1418
- return `${root}-plainTime`;
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);
1419
1633
  default:
1420
- return `${root}-${obj.kind}`;
1634
+ return `${root}-${type.kind}`;
1421
1635
  }
1422
1636
  }
1423
1637
  /**