@azure-tools/typespec-rust 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +68 -11
  43. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  44. package/dist/src/tcgcadapter/adapter.js +421 -190
  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;
@@ -379,39 +393,96 @@ export class Adapter {
379
393
  }
380
394
  return this.getStringType();
381
395
  }
396
+ case 'nullable':
397
+ // TODO: workaround until https://github.com/Azure/typespec-rust/issues/42 is fixed
398
+ return this.getType(type.type, stack);
382
399
  case 'offsetDateTime': {
383
- const keyName = `${type.kind}-${type.encode}`;
400
+ const encoding = getDateTimeEncoding(type.encode);
401
+ const keyName = `offsetDateTime-${encoding}`;
384
402
  let timeType = this.types.get(keyName);
385
403
  if (timeType) {
386
404
  return timeType;
387
405
  }
388
- timeType = new rust.OffsetDateTime(this.crate, getDateTimeEncoding(type.encode), false);
406
+ timeType = new rust.OffsetDateTime(this.crate, encoding, false);
389
407
  this.types.set(keyName, timeType);
390
408
  return timeType;
391
409
  }
392
410
  case 'unknown': {
393
- let anyType = this.types.get(type.kind);
411
+ const keyName = 'jsonValue';
412
+ let anyType = this.types.get(keyName);
394
413
  if (anyType) {
395
414
  return anyType;
396
415
  }
397
416
  anyType = new rust.JsonValue(this.crate);
398
- this.types.set(type.kind, anyType);
417
+ this.types.set(keyName, anyType);
399
418
  return anyType;
400
419
  }
401
420
  case 'utcDateTime': {
402
- const keyName = `${type.kind}-${type.encode}`;
421
+ const encoding = getDateTimeEncoding(type.encode);
422
+ const keyName = `offsetDateTime-${encoding}-utc`;
403
423
  let timeType = this.types.get(keyName);
404
424
  if (timeType) {
405
425
  return timeType;
406
426
  }
407
- timeType = new rust.OffsetDateTime(this.crate, getDateTimeEncoding(type.encode), true);
427
+ timeType = new rust.OffsetDateTime(this.crate, encoding, true);
408
428
  this.types.set(keyName, timeType);
409
429
  return timeType;
410
430
  }
411
431
  default:
412
- throw new 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);
413
433
  }
414
434
  }
435
+ /** returns an EncodedBytes type with the specified encoding */
436
+ getEncodedBytes(encoding, asSlice) {
437
+ const keyName = `encodedBytes-${encoding}${asSlice ? '-slice' : ''}`;
438
+ let encodedBytesType = this.types.get(keyName);
439
+ if (encodedBytesType) {
440
+ return encodedBytesType;
441
+ }
442
+ encodedBytesType = new rust.EncodedBytes(encoding, asSlice);
443
+ this.types.set(keyName, encodedBytesType);
444
+ return encodedBytesType;
445
+ }
446
+ /** returns a HashMap<String, type> */
447
+ getHashMap(type) {
448
+ const keyName = recursiveKeyName('hashmap', type);
449
+ let hashmapType = this.types.get(keyName);
450
+ if (hashmapType) {
451
+ return hashmapType;
452
+ }
453
+ hashmapType = new rust.HashMap(type);
454
+ this.types.set(keyName, hashmapType);
455
+ return hashmapType;
456
+ }
457
+ /** returns the specified type wrapped in a Ref */
458
+ getRefType(type) {
459
+ const typeKey = recursiveKeyName('ref', type);
460
+ let refType = this.types.get(typeKey);
461
+ if (!refType) {
462
+ refType = new rust.Ref(type);
463
+ this.types.set(typeKey, refType);
464
+ }
465
+ return refType;
466
+ }
467
+ /** returns a scalar for the specified scalar type */
468
+ getScalar(type) {
469
+ let scalar = this.types.get(type);
470
+ if (!scalar) {
471
+ scalar = new rust.Scalar(type);
472
+ this.types.set(type, scalar);
473
+ }
474
+ return scalar;
475
+ }
476
+ /** returns a slice of the specified type */
477
+ getSlice(type) {
478
+ const typeKey = recursiveKeyName('slice', type);
479
+ let slice = this.types.get(typeKey);
480
+ if (!slice) {
481
+ slice = new rust.Slice(type);
482
+ this.types.set(typeKey, slice);
483
+ }
484
+ return slice;
485
+ }
415
486
  /** returns the Rust string slice type */
416
487
  getStringSlice() {
417
488
  const typeKey = 'str';
@@ -445,6 +516,17 @@ export class Adapter {
445
516
  this.types.set(typeKey, unitType);
446
517
  return unitType;
447
518
  }
519
+ /** returns a Vec<type> */
520
+ getVec(type) {
521
+ const keyName = recursiveKeyName('Vec', type);
522
+ let vectorType = this.types.get(keyName);
523
+ if (vectorType) {
524
+ return vectorType;
525
+ }
526
+ vectorType = new rust.Vector(type);
527
+ this.types.set(keyName, vectorType);
528
+ return vectorType;
529
+ }
448
530
  /**
449
531
  * converts a tcgc constant to a Rust literal
450
532
  *
@@ -494,7 +576,7 @@ export class Adapter {
494
576
  * @returns a Rust client
495
577
  */
496
578
  recursiveAdaptClient(client, parent) {
497
- var _a;
579
+ var _a, _b, _c, _d, _e;
498
580
  let clientName = client.name;
499
581
  // NOTE: if the client has the @clientName decorator applied then use that verbatim
500
582
  if (parent && !client.decorators.find((decorator) => decorator.name === 'Azure.ClientGenerator.Core.@clientName')) {
@@ -561,7 +643,7 @@ export class Adapter {
561
643
  }
562
644
  default:
563
645
  if (throwOnDefault) {
564
- throw new Error(`credential scheme type ${cred.type} NYI`);
646
+ throw new AdapterError('UnsupportedTsp', `credential scheme type ${cred.type} NYI`);
565
647
  }
566
648
  return AuthTypes.Default;
567
649
  }
@@ -585,7 +667,7 @@ export class Adapter {
585
667
  }
586
668
  // no supported credential types were specified
587
669
  if (authType === AuthTypes.Default) {
588
- throw new 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);
589
671
  }
590
672
  continue;
591
673
  }
@@ -613,7 +695,7 @@ export class Adapter {
613
695
  // note that the types of the param and the field are different.
614
696
  // NOTE: we use param.name here instead of templateArg.name as
615
697
  // the former has the fixed name "endpoint" which is what we want.
616
- const adaptedParam = new rust.ClientMethodParameter(param.name, this.getStringSlice(), false, true);
698
+ const adaptedParam = new rust.ClientMethodParameter(param.name, this.getRefType(this.getStringSlice()), false);
617
699
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
618
700
  ctorParams.push(adaptedParam);
619
701
  rustClient.fields.push(new rust.StructField(param.name, 'pubCrate', new rust.Url(this.crate)));
@@ -642,9 +724,9 @@ export class Adapter {
642
724
  }
643
725
  const clientParam = this.adaptClientParameter(templateArg, rustClient.constructable);
644
726
  if (clientParam.kind !== 'clientEndpoint') {
645
- throw new 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);
646
728
  }
647
- (_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);
648
730
  ctorParams.push(clientParam);
649
731
  }
650
732
  break;
@@ -691,7 +773,7 @@ export class Adapter {
691
773
  }
692
774
  }
693
775
  else {
694
- throw new Error(`uninstantiable client ${client.name} has no parent`);
776
+ throw new AdapterError('InternalError', `uninstantiable client ${client.name} has no parent`);
695
777
  }
696
778
  for (const child of values(client.children)) {
697
779
  const subClient = this.recursiveAdaptClient(child, rustClient);
@@ -705,7 +787,7 @@ export class Adapter {
705
787
  code: 'LroNotSupported',
706
788
  severity: 'warning',
707
789
  message: `skip emitting LRO ${method.name}`,
708
- target: method.__raw ? method.__raw.node : NoTarget,
790
+ target: (_e = (_d = method.__raw) === null || _d === void 0 ? void 0 : _d.node) !== null && _e !== void 0 ? _e : NoTarget,
709
791
  });
710
792
  continue;
711
793
  }
@@ -723,12 +805,15 @@ export class Adapter {
723
805
  */
724
806
  createTokenCredentialCtor(rustClient, cred) {
725
807
  if (cred.flows.length === 0) {
726
- throw new Error(`no flows defined for credential type ${cred.type}`);
808
+ throw new AdapterError('InternalError', `no flows defined for credential type ${cred.type}`, cred.model);
727
809
  }
728
810
  const scopes = new Array();
729
811
  for (const scope of cred.flows[0].scopes) {
730
812
  scopes.push(scope.value);
731
813
  }
814
+ if (scopes.length === 0) {
815
+ throw new AdapterError('InternalError', 'scopes must contain at least one entry', cred.model);
816
+ }
732
817
  const ctorTokenCredential = new rust.Constructor('new');
733
818
  const tokenCredParam = new rust.ClientMethodParameter('credential', new rust.Arc(new rust.TokenCredential(this.crate, scopes)), false);
734
819
  tokenCredParam.docs.summary = `An implementation of [\`TokenCredential\`](azure_core::credentials::TokenCredential) that can provide an Entra ID token to use when authenticating.`;
@@ -744,10 +829,11 @@ export class Adapter {
744
829
  * @returns the Rust client parameter
745
830
  */
746
831
  adaptClientParameter(param, constructable) {
832
+ var _a;
747
833
  let paramType;
748
834
  if (param.isApiVersionParam) {
749
835
  if (!param.clientDefaultValue) {
750
- throw new 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);
751
837
  }
752
838
  paramType = this.getStringType();
753
839
  }
@@ -816,6 +902,7 @@ export class Adapter {
816
902
  * @param rustClient the client to which the method belongs
817
903
  */
818
904
  adaptMethod(method, rustClient) {
905
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
819
906
  let srcMethodName = method.name;
820
907
  if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
821
908
  const chunks = codegen.deconstruct(srcMethodName);
@@ -826,11 +913,11 @@ export class Adapter {
826
913
  code: 'PagingMethodRename',
827
914
  severity: 'warning',
828
915
  message: `renamed paging method from ${method.name} to ${srcMethodName}`,
829
- target: method.__raw ? method.__raw.node : NoTarget,
916
+ target: (_b = (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : NoTarget,
830
917
  });
831
918
  }
832
919
  else if (this.renamedMethods.has(srcMethodName)) {
833
- throw new 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);
834
921
  }
835
922
  const methodName = naming.getEscapedReservedName(snakeCaseName(srcMethodName), 'fn');
836
923
  const optionsLifetime = new rust.Lifetime('a');
@@ -865,7 +952,7 @@ export class Adapter {
865
952
  rustMethod = new rust.PageableMethod(methodName, rustClient, pub, methodOptions, httpMethod, httpPath);
866
953
  break;
867
954
  default:
868
- throw new 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);
869
956
  }
870
957
  rustMethod.docs = this.adaptDocs(method.summary, method.doc);
871
958
  rustClient.methods.push(rustMethod);
@@ -875,6 +962,8 @@ export class Adapter {
875
962
  if (method.operation.bodyParam) {
876
963
  allOpParams.push(method.operation.bodyParam);
877
964
  }
965
+ // maps tcgc method header/query params to their Rust method params
966
+ const paramsMap = new Map();
878
967
  for (const param of method.parameters) {
879
968
  // we need to translate from the method param to its underlying operation param.
880
969
  // most params have a one-to-one mapping. however, for spread params, there will
@@ -886,7 +975,7 @@ export class Adapter {
886
975
  }).any();
887
976
  }).first();
888
977
  if (!opParam) {
889
- throw new 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);
890
979
  }
891
980
  let adaptedParam;
892
981
  // for spread params there are two cases we need to consider.
@@ -900,6 +989,9 @@ export class Adapter {
900
989
  else {
901
990
  adaptedParam = this.adaptMethodParameter(opParam);
902
991
  }
992
+ if (adaptedParam.kind === 'header' || adaptedParam.kind === 'query') {
993
+ paramsMap.set(param, adaptedParam);
994
+ }
903
995
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
904
996
  rustMethod.params.push(adaptedParam);
905
997
  if (adaptedParam.optional) {
@@ -911,18 +1003,11 @@ export class Adapter {
911
1003
  // already wrapped in an Option<T> type.
912
1004
  const field = adaptedParam.type.content.type.fields.find(f => { return f.name === adaptedParam.name; });
913
1005
  if (!field) {
914
- throw new 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}`);
915
1007
  }
916
1008
  fieldType = field.type;
917
1009
  }
918
1010
  else {
919
- switch (adaptedParam.type.kind) {
920
- case 'requestContent':
921
- case 'struct':
922
- break;
923
- default:
924
- adaptedParam.type = this.typeToWireType(adaptedParam.type);
925
- }
926
1011
  fieldType = new rust.Option(adaptedParam.type);
927
1012
  }
928
1013
  const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
@@ -940,17 +1025,18 @@ export class Adapter {
940
1025
  }
941
1026
  }
942
1027
  const getBodyFormat = () => {
1028
+ var _a, _b;
943
1029
  // fetch the body format from the HTTP responses.
944
1030
  // they should all have the same type so no need to match responses to type.
945
1031
  let defaultContentType;
946
1032
  for (const httpResp of method.operation.responses) {
947
1033
  if (defaultContentType && httpResp.defaultContentType && defaultContentType !== httpResp.defaultContentType) {
948
- throw new 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);
949
1035
  }
950
1036
  defaultContentType = httpResp.defaultContentType;
951
1037
  }
952
1038
  if (!defaultContentType) {
953
- 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);
954
1040
  }
955
1041
  return this.adaptBodyFormat(defaultContentType);
956
1042
  };
@@ -980,16 +1066,53 @@ export class Adapter {
980
1066
  // however, we want the synthesized paged response envelope type instead.
981
1067
  const synthesizedType = method.operation.responses[0].type;
982
1068
  if (!synthesizedType) {
983
- throw new 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);
984
1070
  }
985
1071
  else if (synthesizedType.kind !== 'model') {
986
- 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);
987
1073
  }
988
1074
  const format = getBodyFormat();
989
- const synthesizedModel = this.getModel(synthesizedType);
1075
+ const synthesizedModel = this.getModel(synthesizedType, new Array());
990
1076
  if (!this.crate.models.includes(synthesizedModel)) {
991
1077
  this.crate.models.push(synthesizedModel);
992
1078
  }
1079
+ // for the pager response type, remove the Option<T> around the Vec<T> for the page items
1080
+ if (!method.pagingMetadata.pageItemsSegments) {
1081
+ throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (_h = method.__raw) === null || _h === void 0 ? void 0 : _h.node);
1082
+ }
1083
+ // unwrap all of the segments for the paged response
1084
+ let unwrappedCount = 0;
1085
+ let typeToUnwrap = synthesizedModel;
1086
+ for (const pageItemsSegment of method.pagingMetadata.pageItemsSegments) {
1087
+ const segment = pageItemsSegment;
1088
+ let serde;
1089
+ switch (format) {
1090
+ case 'json':
1091
+ serde = segment.serializationOptions.json.name;
1092
+ break;
1093
+ case 'xml':
1094
+ serde = segment.serializationOptions.xml.name;
1095
+ break;
1096
+ }
1097
+ for (let i = 0; i < typeToUnwrap.fields.length; ++i) {
1098
+ const field = typeToUnwrap.fields[i];
1099
+ if (field.serde === serde) {
1100
+ // check if this has already been unwrapped (e.g. type is shared across operations)
1101
+ if (field.type.kind === 'option') {
1102
+ typeToUnwrap.fields[i].type = typeToUnwrap.fields[i].type.type;
1103
+ }
1104
+ // move to the next segment
1105
+ if (field.type.kind === 'model') {
1106
+ typeToUnwrap = field.type;
1107
+ }
1108
+ ++unwrappedCount;
1109
+ break;
1110
+ }
1111
+ }
1112
+ }
1113
+ if (unwrappedCount !== method.pagingMetadata.pageItemsSegments.length) {
1114
+ throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (_j = method.__raw) === null || _j === void 0 ? void 0 : _j.node);
1115
+ }
993
1116
  returnType = new rust.Pager(this.crate, new rust.Payload(synthesizedModel, format));
994
1117
  }
995
1118
  else if (method.response.type && !(method.response.type.kind === 'bytes' && method.response.type.encode === 'bytes')) {
@@ -1011,14 +1134,42 @@ export class Adapter {
1011
1134
  returnType = new rust.Response(this.crate, this.getUnitType());
1012
1135
  }
1013
1136
  rustMethod.returns = new rust.Result(this.crate, returnType);
1014
- rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, responseHeaders);
1137
+ const responseHeadersMap = this.adaptResposeHeaders(responseHeaders);
1138
+ rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, Array.from(responseHeadersMap.values()));
1015
1139
  if (method.kind === 'paging') {
1016
1140
  // can't do this until the method has been completely adapted
1017
- rustMethod.strategy = this.adaptPageableMethodStrategy(method);
1141
+ rustMethod.strategy = this.adaptPageableMethodStrategy(method, paramsMap, responseHeadersMap);
1142
+ }
1143
+ }
1144
+ /**
1145
+ * adapts response headers into Rust response headers and provides
1146
+ * a mapping from the tcgc response header to the Rust equivalent.
1147
+ * if there are no headers to adapt, an empty map is returned.
1148
+ *
1149
+ * @param responseHeaders the response headers to adapt (can be empty)
1150
+ * @returns the map of response headers
1151
+ */
1152
+ adaptResposeHeaders(responseHeaders) {
1153
+ const responseHeadersMap = new Map();
1154
+ // adapt the response headers and add them to the trait
1155
+ for (const header of responseHeaders) {
1156
+ let responseHeader;
1157
+ if (header.type.kind === 'dict') {
1158
+ if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
1159
+ throw new AdapterError('InternalError', `unexpected response header collection ${header.serializedName}`, header.__raw.node);
1160
+ }
1161
+ responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
1162
+ }
1163
+ else {
1164
+ responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.typeToWireType(this.getType(header.type)));
1165
+ }
1166
+ responseHeader.docs = this.adaptDocs(header.summary, header.doc);
1167
+ responseHeadersMap.set(header, responseHeader);
1018
1168
  }
1169
+ return responseHeadersMap;
1019
1170
  }
1020
1171
  /**
1021
- * adapts response headers into a Rust ResponseHeadersTrait
1172
+ * creates a Rust ResponseHeadersTrait for the specified response headers.
1022
1173
  * if there are no response headers, undefined is returned.
1023
1174
  *
1024
1175
  * @param client the client that contains the method
@@ -1030,6 +1181,32 @@ export class Adapter {
1030
1181
  if (responseHeaders.length === 0) {
1031
1182
  return undefined;
1032
1183
  }
1184
+ /**
1185
+ * recursively builds a name from the specified type.
1186
+ * e.g. Vec<FooModel> would be VecFooModel etc.
1187
+ *
1188
+ * @param type the type for which to build a name
1189
+ * @returns the name
1190
+ */
1191
+ const recursiveTypeName = function (type) {
1192
+ switch (type.kind) {
1193
+ case 'enum':
1194
+ case 'model':
1195
+ return type.name;
1196
+ case 'hashmap':
1197
+ return `${type.name}${recursiveTypeName(type.type)}`;
1198
+ case 'ref':
1199
+ return `Ref${recursiveTypeName(type.type)}`;
1200
+ case 'scalar':
1201
+ return codegen.capitalize(type.type);
1202
+ case 'slice':
1203
+ return `Slice${recursiveTypeName(type.type)}`;
1204
+ case 'Vec':
1205
+ return `${type.kind}${recursiveTypeName(type.type)}`;
1206
+ default:
1207
+ return codegen.capitalize(type.kind);
1208
+ }
1209
+ };
1033
1210
  // response header traits are only ever for marker types and payloads
1034
1211
  let implFor;
1035
1212
  switch (method.returns.type.kind) {
@@ -1038,13 +1215,13 @@ export class Adapter {
1038
1215
  break;
1039
1216
  case 'response':
1040
1217
  if (method.returns.type.content.kind !== 'marker' && method.returns.type.content.kind !== 'payload') {
1041
- throw new Error(`unexpected method content kind ${method.returns.type.content.kind}`);
1218
+ throw new AdapterError('InternalError', `unexpected method content kind ${method.returns.type.content.kind}`);
1042
1219
  }
1043
1220
  implFor = method.returns.type.content;
1044
1221
  break;
1045
1222
  default:
1046
1223
  // this is Unit which should have been previously skipped
1047
- throw new Error(`unexpected method return kind ${method.returns.type.kind}`);
1224
+ throw new AdapterError('InternalError', `unexpected method return kind ${method.returns.type.kind}`);
1048
1225
  }
1049
1226
  let traitName;
1050
1227
  switch (implFor.kind) {
@@ -1052,83 +1229,99 @@ export class Adapter {
1052
1229
  traitName = implFor.name;
1053
1230
  break;
1054
1231
  case 'payload':
1055
- traitName = this.recursiveTypeName(implFor.type);
1232
+ traitName = recursiveTypeName(implFor.type);
1056
1233
  break;
1057
1234
  }
1058
1235
  traitName += 'Headers';
1059
1236
  // NOTE: the complete doc text will be emitted at codegen time
1060
1237
  const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
1061
1238
  const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs);
1062
- // adapt the response headers and add them to the trait
1063
- for (const header of responseHeaders) {
1064
- let responseHeader;
1065
- if (header.type.kind === 'dict') {
1066
- if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
1067
- throw new Error(`unexpected response header collection ${header.serializedName}`);
1068
- }
1069
- responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
1070
- }
1071
- else {
1072
- responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.getType(header.type));
1073
- }
1074
- responseHeader.docs = this.adaptDocs(header.summary, header.doc);
1075
- responseHeadersTrait.headers.push(responseHeader);
1076
- }
1239
+ responseHeadersTrait.headers.push(...responseHeaders);
1077
1240
  return responseHeadersTrait;
1078
1241
  }
1079
- /**
1080
- * recursively builds a name from the specified type.
1081
- * e.g. Vec<FooModel> would be VecFooModel etc.
1082
- *
1083
- * @param type the type for which to build a name
1084
- * @returns the name
1085
- */
1086
- recursiveTypeName(type) {
1087
- switch (type.kind) {
1088
- case 'enum':
1089
- case 'model':
1090
- return type.name;
1091
- case 'hashmap':
1092
- return `${type.name}${this.recursiveTypeName(type.type)}`;
1093
- case 'Vec':
1094
- return `${type.kind}${this.recursiveTypeName(type.type)}`;
1095
- default:
1096
- return codegen.capitalize(type.kind);
1097
- }
1098
- }
1099
1242
  /**
1100
1243
  * creates the pageable strategy based on the method definition
1101
1244
  *
1102
1245
  * @param method the pageable method for which to create a strategy
1246
+ * @param paramsMap maps tcgc method params to Rust params (needed for continuation token strategy)
1247
+ * @param respHeadersMap maps tcgc response headers to Rust response headers (needed for continuation token strategy)
1103
1248
  * @returns the pageable strategy
1104
1249
  */
1105
- adaptPageableMethodStrategy(method) {
1250
+ adaptPageableMethodStrategy(method, paramsMap, respHeadersMap) {
1251
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1106
1252
  if (method.pagingMetadata.nextLinkOperation) {
1107
1253
  // TODO: https://github.com/Azure/autorest.rust/issues/103
1108
- throw new 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);
1109
1255
  }
1110
1256
  else if (method.pagingMetadata.nextLinkSegments) {
1111
1257
  if (method.pagingMetadata.nextLinkSegments.length > 1) {
1112
1258
  // TODO: https://github.com/Azure/autorest.rust/issues/102
1113
- throw new 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);
1114
1260
  }
1115
1261
  // this is the field in the response type that contains the next link URL
1116
1262
  const nextLinkSegment = method.pagingMetadata.nextLinkSegments[0];
1117
1263
  if (nextLinkSegment.kind !== 'property') {
1118
- throw new 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);
1119
1265
  }
1120
1266
  const nextLinkField = this.fieldsMap.get(nextLinkSegment);
1121
1267
  if (!nextLinkField) {
1122
1268
  // the most likely explanation for this is lack of reference equality
1123
- throw new 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);
1124
1270
  }
1125
1271
  return new rust.PageableStrategyNextLink(nextLinkField);
1126
1272
  }
1127
1273
  else if (method.pagingMetadata.continuationTokenParameterSegments && method.pagingMetadata.continuationTokenResponseSegments) {
1128
- 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);
1129
1322
  }
1130
1323
  else {
1131
- 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);
1132
1325
  }
1133
1326
  }
1134
1327
  /**
@@ -1138,6 +1331,7 @@ export class Adapter {
1138
1331
  * @returns a Rust method parameter
1139
1332
  */
1140
1333
  adaptMethodParameter(param) {
1334
+ var _a, _b, _c, _d, _e;
1141
1335
  const paramLoc = param.onClient ? 'client' : 'method';
1142
1336
  /**
1143
1337
  * used to create keys for this.clientMethodParams
@@ -1158,11 +1352,12 @@ export class Adapter {
1158
1352
  }
1159
1353
  const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
1160
1354
  let paramType = this.getType(param.type);
1161
- let paramByRef = false;
1162
- // for required header/path/query method string params, we emit them as &str instead of String
1163
- if (!param.optional && !param.onClient && paramType.kind === 'String' && (param.kind === 'header' || param.kind === 'path' || param.kind === 'query')) {
1164
- paramType = this.getStringSlice();
1165
- 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
+ }
1166
1361
  }
1167
1362
  let adaptedParam;
1168
1363
  switch (param.kind) {
@@ -1180,11 +1375,11 @@ export class Adapter {
1180
1375
  }
1181
1376
  case 'cookie':
1182
1377
  // TODO: https://github.com/Azure/typespec-rust/issues/192
1183
- throw new 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);
1184
1379
  case 'header':
1185
1380
  if (param.collectionFormat) {
1186
- if (paramType.kind !== 'Vec') {
1187
- 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);
1188
1383
  }
1189
1384
  let format;
1190
1385
  switch (param.collectionFormat) {
@@ -1198,47 +1393,91 @@ export class Adapter {
1198
1393
  format = param.collectionFormat;
1199
1394
  break;
1200
1395
  default:
1201
- throw new 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);
1202
1397
  }
1203
1398
  adaptedParam = new rust.HeaderCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, format);
1204
1399
  }
1205
1400
  else if (param.serializedName === 'x-ms-meta') {
1206
1401
  if (paramType.kind !== 'hashmap') {
1207
- throw new 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);
1208
1403
  }
1209
1404
  adaptedParam = new rust.HeaderHashMapParameter(paramName, param.serializedName, paramLoc, param.optional, paramType);
1210
1405
  }
1211
1406
  else {
1212
- adaptedParam = new rust.HeaderParameter(paramName, param.serializedName, paramLoc, param.optional, paramType);
1407
+ adaptedParam = new rust.HeaderParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType));
1213
1408
  adaptedParam.isApiVersion = param.isApiVersionParam;
1214
1409
  }
1215
1410
  break;
1216
1411
  case 'path':
1217
- adaptedParam = new rust.PathParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, param.allowReserved);
1412
+ adaptedParam = new rust.PathParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType), param.allowReserved);
1218
1413
  break;
1219
1414
  case 'query':
1220
1415
  if (param.collectionFormat) {
1221
1416
  const format = param.collectionFormat === 'simple' ? 'csv' : (param.collectionFormat === 'form' ? 'multi' : param.collectionFormat);
1222
- if (paramType.kind !== 'Vec') {
1223
- throw new 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);
1224
1419
  }
1225
1420
  // TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
1226
1421
  adaptedParam = new rust.QueryCollectionParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, true, format);
1227
1422
  }
1228
1423
  else {
1229
1424
  // TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
1230
- adaptedParam = new rust.QueryParameter(paramName, param.serializedName, paramLoc, param.optional, paramType, true);
1425
+ adaptedParam = new rust.QueryParameter(paramName, param.serializedName, paramLoc, param.optional, this.typeToWireType(paramType), true);
1231
1426
  adaptedParam.isApiVersion = param.isApiVersionParam;
1232
1427
  }
1233
1428
  break;
1234
1429
  }
1235
1430
  adaptedParam.docs = this.adaptDocs(param.summary, param.doc);
1236
- adaptedParam.ref = paramByRef;
1237
1431
  if (paramLoc === 'client') {
1238
1432
  this.clientMethodParams.set(getClientParamsKey(param), adaptedParam);
1239
1433
  }
1240
1434
  return adaptedParam;
1241
1435
  }
1436
+ /**
1437
+ * updates the specified type to a borrowed type based on its type and kind.
1438
+ * if no such transformation is necessary, undefined is returned.
1439
+ * e.g. a String param that doesn't need to be owned will be
1440
+ * returned as a &str.
1441
+ *
1442
+ * @param type the param type to be updated
1443
+ * @param kind the kind of param
1444
+ * @returns the updated param type or undefined
1445
+ */
1446
+ canBorrowMethodParam(type, kind) {
1447
+ const recursiveBuildVecStr = (v) => {
1448
+ switch (v.kind) {
1449
+ case 'encodedBytes':
1450
+ return this.getRefType(this.getEncodedBytes(v.encoding, true));
1451
+ case 'hashmap':
1452
+ return this.getHashMap(this.typeToWireType(recursiveBuildVecStr(v.type)));
1453
+ case 'String':
1454
+ return this.getRefType(this.getStringSlice());
1455
+ case 'Vec':
1456
+ return this.getVec(this.typeToWireType(recursiveBuildVecStr(v.type)));
1457
+ default:
1458
+ throw new AdapterError('InternalError', `unexpected kind ${v.kind}`);
1459
+ }
1460
+ };
1461
+ switch (type.kind) {
1462
+ case 'String':
1463
+ // header String params are always owned
1464
+ if (kind !== 'header') {
1465
+ return this.getRefType(this.getStringSlice());
1466
+ }
1467
+ break;
1468
+ case 'Vec': {
1469
+ // if this is an array of string, we ultimately want a slice of &str
1470
+ const unwrapped = helpers.unwrapVec(type);
1471
+ if (unwrapped.kind === 'String' || unwrapped.kind === 'encodedBytes') {
1472
+ return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)));
1473
+ }
1474
+ return this.getRefType(this.getSlice(type.type));
1475
+ }
1476
+ case 'encodedBytes':
1477
+ return this.getRefType(this.getEncodedBytes(type.encoding, true));
1478
+ }
1479
+ return undefined;
1480
+ }
1242
1481
  /**
1243
1482
  * narrows a rust.Type to a rust.WireType.
1244
1483
  * if type isn't a rust.WireType, an error is thrown.
@@ -1249,6 +1488,7 @@ export class Adapter {
1249
1488
  typeToWireType(type) {
1250
1489
  switch (type.kind) {
1251
1490
  case 'bytes':
1491
+ case 'decimal':
1252
1492
  case 'encodedBytes':
1253
1493
  case 'enum':
1254
1494
  case 'enumValue':
@@ -1258,14 +1498,16 @@ export class Adapter {
1258
1498
  case 'literal':
1259
1499
  case 'model':
1260
1500
  case 'offsetDateTime':
1501
+ case 'ref':
1261
1502
  case 'scalar':
1503
+ case 'slice':
1262
1504
  case 'str':
1263
1505
  case 'String':
1264
1506
  case 'Url':
1265
1507
  case 'Vec':
1266
1508
  return type;
1267
1509
  default:
1268
- throw new Error(`cannot convert ${type.kind} to a wire type`);
1510
+ throw new AdapterError('InternalError', `cannot convert ${type.kind} to a wire type`);
1269
1511
  }
1270
1512
  }
1271
1513
  /**
@@ -1277,6 +1519,7 @@ export class Adapter {
1277
1519
  * @returns a Rust partial body parameter
1278
1520
  */
1279
1521
  adaptMethodSpreadParameter(param, format, opParamType) {
1522
+ var _a, _b;
1280
1523
  switch (format) {
1281
1524
  case 'json':
1282
1525
  case 'xml': {
@@ -1289,27 +1532,16 @@ export class Adapter {
1289
1532
  }
1290
1533
  }
1291
1534
  if (serializedName === undefined) {
1292
- throw new Error(`didn't find body model property for spread parameter ${param.name}`);
1293
- }
1294
- // this is the param type as surfaced in the method sig.
1295
- // it's usually the same type as its corresponding field type
1296
- // in the underlying model. the only exception is for String
1297
- // types. we want to surface those as &str in the method sig.
1298
- let paramType = this.getType(param.type);
1299
- let paramByRef = false;
1300
- if (!param.optional && !param.onClient && paramType.kind === 'String') {
1301
- paramType = this.getStringSlice();
1302
- paramByRef = true;
1535
+ throw new AdapterError('InternalError', `didn't find body model property for spread parameter ${param.name}`, (_a = param.__raw) === null || _a === void 0 ? void 0 : _a.node);
1303
1536
  }
1304
1537
  // this is the internal model type that the spread params coalesce into
1305
1538
  const payloadType = this.getType(opParamType);
1306
1539
  if (payloadType.kind !== 'model') {
1307
- throw new 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);
1308
1541
  }
1309
1542
  const paramName = naming.getEscapedReservedName(snakeCaseName(param.name), 'param');
1310
1543
  const paramLoc = 'method';
1311
- const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, paramType, new rust.RequestContent(this.crate, new rust.Payload(payloadType, format)));
1312
- adaptedParam.ref = paramByRef;
1544
+ const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, new rust.Payload(payloadType, format)));
1313
1545
  return adaptedParam;
1314
1546
  }
1315
1547
  }
@@ -1332,10 +1564,14 @@ export class Adapter {
1332
1564
  return 'xml';
1333
1565
  }
1334
1566
  else {
1335
- throw new Error(`unexpected contentType ${contentType}`);
1567
+ throw new AdapterError('InternalError', `unexpected contentType ${contentType}`);
1336
1568
  }
1337
1569
  }
1338
1570
  }
1571
+ /** typeguard to determine if type is a Ref<Slice> */
1572
+ function isRefSlice(type) {
1573
+ return type.kind === 'ref' && type.type.kind === 'slice';
1574
+ }
1339
1575
  /**
1340
1576
  * transforms Etag etc to all lower case.
1341
1577
  * this is to prevent inadvertently snake-casing
@@ -1366,41 +1602,36 @@ function snakeCaseName(name) {
1366
1602
  *
1367
1603
  * obj is recursively unwrapped, and each layer is used to construct
1368
1604
  * the key. e.g. if obj is a HashMap<String, Vec<i32>> this would
1369
- * unwrap to dict-array-int32.
1605
+ * unwrap to hashmap-Vec-i32.
1370
1606
  *
1371
1607
  * @param root the starting value for the key
1372
1608
  * @param obj the type for which to create the key
1373
1609
  * @returns a string containing the complete map key
1374
1610
  */
1375
- function recursiveKeyName(root, obj) {
1376
- switch (obj.kind) {
1377
- case 'array':
1378
- return recursiveKeyName(`${root}-array`, obj.valueType);
1379
- case 'bytes':
1380
- 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' : ''}`;
1381
1617
  case 'enum':
1382
- return `${root}-${obj.name}`;
1383
- case 'enumvalue':
1384
- return `${root}-${obj.enumType.name}-${obj.value}`;
1385
- case 'dict':
1386
- return recursiveKeyName(`${root}-dict`, obj.valueType);
1387
- case 'plainDate':
1388
- return `${root}-plainDate`;
1389
- case 'utcDateTime':
1390
- return `${root}-${obj.encode}`;
1391
- case 'duration':
1392
- // TODO: this should be: return `${root}-duration-${obj.encode}`;
1393
- // as it is now, it treats the duration as a String
1394
- // https://github.com/Azure/typespec-rust/issues/41
1395
- return `${root}-${obj.wireType.kind}`;
1618
+ return `${root}-${type.kind}-${type.name}`;
1619
+ case 'enumValue':
1620
+ return `${root}-${type.type.name}-${type.name}`;
1621
+ case 'hashmap':
1622
+ return recursiveKeyName(`${root}-${type.kind}`, type.type);
1623
+ case 'offsetDateTime':
1624
+ return `${root}-${type.kind}-${type.encoding}${type.utc ? '-utc' : ''}`;
1396
1625
  case 'model':
1397
- return `${root}-${obj.name}`;
1398
- case 'nullable':
1399
- return recursiveKeyName(root, obj.type);
1400
- case 'plainTime':
1401
- 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);
1402
1633
  default:
1403
- return `${root}-${obj.kind}`;
1634
+ return `${root}-${type.kind}`;
1404
1635
  }
1405
1636
  }
1406
1637
  /**