@azure-tools/typespec-rust 0.13.3 → 0.14.1

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