@azure-tools/typespec-rust 0.13.0 → 0.13.2

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 (44) hide show
  1. package/dist/src/codegen/clients.d.ts.map +1 -1
  2. package/dist/src/codegen/clients.js +14 -14
  3. package/dist/src/codegen/clients.js.map +1 -1
  4. package/dist/src/codegen/context.js +6 -6
  5. package/dist/src/codegen/context.js.map +1 -1
  6. package/dist/src/codegen/enums.js +2 -2
  7. package/dist/src/codegen/enums.js.map +1 -1
  8. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  9. package/dist/src/codegen/headerTraits.js +48 -56
  10. package/dist/src/codegen/headerTraits.js.map +1 -1
  11. package/dist/src/codegen/helpers.d.ts +4 -3
  12. package/dist/src/codegen/helpers.d.ts.map +1 -1
  13. package/dist/src/codegen/helpers.js +14 -7
  14. package/dist/src/codegen/helpers.js.map +1 -1
  15. package/dist/src/codegen/lib.js +1 -1
  16. package/dist/src/codegen/lib.js.map +1 -1
  17. package/dist/src/codegen/models.js +15 -14
  18. package/dist/src/codegen/models.js.map +1 -1
  19. package/dist/src/codegen/use.d.ts +4 -11
  20. package/dist/src/codegen/use.d.ts.map +1 -1
  21. package/dist/src/codegen/use.js +103 -62
  22. package/dist/src/codegen/use.js.map +1 -1
  23. package/dist/src/codemodel/client.d.ts +30 -10
  24. package/dist/src/codemodel/client.d.ts.map +1 -1
  25. package/dist/src/codemodel/client.js +14 -5
  26. package/dist/src/codemodel/client.js.map +1 -1
  27. package/dist/src/codemodel/crate.js +2 -1
  28. package/dist/src/codemodel/crate.js.map +1 -1
  29. package/dist/src/codemodel/types.d.ts +54 -44
  30. package/dist/src/codemodel/types.d.ts.map +1 -1
  31. package/dist/src/codemodel/types.js +37 -65
  32. package/dist/src/codemodel/types.js.map +1 -1
  33. package/dist/src/emitter.d.ts.map +1 -1
  34. package/dist/src/emitter.js +2 -1
  35. package/dist/src/emitter.js.map +1 -1
  36. package/dist/src/lib.d.ts +1 -0
  37. package/dist/src/lib.d.ts.map +1 -1
  38. package/dist/src/lib.js +1 -0
  39. package/dist/src/lib.js.map +1 -1
  40. package/dist/src/tcgcadapter/adapter.d.ts +45 -0
  41. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  42. package/dist/src/tcgcadapter/adapter.js +215 -55
  43. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  44. package/package.json +24 -24
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/tcgcadapter/adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAyB,WAAW,EAAY,MAAM,oBAAoB,CAAC;AAIlF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,2DAA2D;AAC3D,qBAAa,OAAO;IAClB;;;;;OAKG;WACU,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IAGtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAG/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IAGvE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAE7C,OAAO;IAcP,wDAAwD;IACxD,WAAW,IAAI,IAAI,CAAC,KAAK;IAgCzB,gDAAgD;IAChD,OAAO,CAAC,SAAS;IAajB,4DAA4D;IAC5D,OAAO,CAAC,UAAU;IAuBlB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAoBf;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAWpB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAkEhB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAuBrB,0CAA0C;IAC1C,OAAO,CAAC,OAAO;IAkLf,yCAAyC;IACzC,OAAO,CAAC,cAAc;IAUtB,mCAAmC;IACnC,OAAO,CAAC,aAAa;IAWrB,iCAAiC;IACjC,OAAO,CAAC,WAAW;IAWnB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAWlB,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IAcpB;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAmO5B;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAuC5B;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAkOnB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IA2G5B;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/tcgcadapter/adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAyB,WAAW,EAAY,MAAM,oBAAoB,CAAC;AAIlF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,2DAA2D;AAC3D,qBAAa,OAAO;IAClB;;;;;OAKG;WACU,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAG7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAG/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IAGvE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAG7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8E;IAExG,OAAO;IAgBP,wDAAwD;IACxD,WAAW,IAAI,IAAI,CAAC,KAAK;IAiCzB,gDAAgD;IAChD,OAAO,CAAC,SAAS;IAajB,4DAA4D;IAC5D,OAAO,CAAC,UAAU;IAuBlB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAoBf;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAWpB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAkEhB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IA4BrB,0CAA0C;IAC1C,OAAO,CAAC,OAAO;IAkLf,yCAAyC;IACzC,OAAO,CAAC,cAAc;IAUtB,mCAAmC;IACnC,OAAO,CAAC,aAAa;IAWrB,iCAAiC;IACjC,OAAO,CAAC,WAAW;IAWnB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAWlB,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IASpB;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IAOjB;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA+O5B;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAwC5B;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAyNnB;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAczB;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IA6BnC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IA2G5B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAaxB"}
@@ -32,18 +32,21 @@ export class Adapter {
32
32
  this.types = new Map();
33
33
  this.clientMethodParams = new Map();
34
34
  this.renamedMethods = new Set();
35
+ this.fieldsMap = new Map();
35
36
  this.ctx = ctx;
37
+ this.options = options;
36
38
  let serviceType = 'data-plane';
37
39
  if (this.ctx.arm === true) {
38
40
  serviceType = 'azure-arm';
39
41
  }
40
- this.crate = new rust.Crate(options['crate-name'], options['crate-version'], serviceType);
42
+ this.crate = new rust.Crate(this.options['crate-name'], this.options['crate-version'], serviceType);
41
43
  }
42
44
  /** performs all the steps to convert tcgc to a crate */
43
45
  tcgcToCrate() {
44
46
  this.adaptTypes();
45
47
  this.adaptClients();
46
- if (this.crate.enums.length > 0 || this.crate.models.length > 0) {
48
+ // marker models don't require serde so exclude them from the check
49
+ if (this.crate.enums.length > 0 || values(this.crate.models).where(e => e.kind === 'model').any()) {
47
50
  this.crate.addDependency(new rust.CrateDependency('serde'));
48
51
  }
49
52
  if (this.crate.clients.length > 0 || this.crate.enums.length > 0 || this.crate.models.length > 0) {
@@ -220,7 +223,7 @@ export class Adapter {
220
223
  // the only exception is for HashMap and Vec since an
221
224
  // empty collection conveys the same semantics.
222
225
  if ((modelVisibility === 'pub' || property.optional) && fieldType.kind !== 'hashmap' && fieldType.kind !== 'Vec') {
223
- fieldType = new rust.Option(fieldType);
226
+ fieldType = new rust.Option(this.typeToWireType(fieldType));
224
227
  }
225
228
  const modelField = new rust.ModelField(naming.getEscapedReservedName(snakeCaseName(property.name), 'prop'), property.serializedName, modelVisibility, fieldType);
226
229
  modelField.docs = this.adaptDocs(property.summary, property.doc);
@@ -230,6 +233,10 @@ export class Adapter {
230
233
  modelField.serde = xmlName;
231
234
  }
232
235
  modelField.xmlKind = getXMLKind(property.decorators, modelField);
236
+ // it's possible for different models to reference the same property definition
237
+ if (!this.fieldsMap.get(property)) {
238
+ this.fieldsMap.set(property, modelField);
239
+ }
233
240
  return modelField;
234
241
  }
235
242
  /** converts a tcgc type to a Rust type */
@@ -296,7 +303,7 @@ export class Adapter {
296
303
  if (vectorType) {
297
304
  return vectorType;
298
305
  }
299
- vectorType = new rust.Vector(this.getType(type.valueType));
306
+ vectorType = new rust.Vector(this.typeToWireType(this.getType(type.valueType)));
300
307
  this.types.set(keyName, vectorType);
301
308
  return vectorType;
302
309
  }
@@ -322,7 +329,7 @@ export class Adapter {
322
329
  if (hashmapType) {
323
330
  return hashmapType;
324
331
  }
325
- hashmapType = new rust.HashMap(this.getType(type.valueType));
332
+ hashmapType = new rust.HashMap(this.typeToWireType(this.getType(type.valueType)));
326
333
  this.types.set(keyName, hashmapType);
327
334
  return hashmapType;
328
335
  }
@@ -457,16 +464,27 @@ export class Adapter {
457
464
  /** converts all tcgc clients and their methods into Rust clients/methods */
458
465
  adaptClients() {
459
466
  for (const client of this.ctx.sdkPackage.clients) {
460
- if (client.methods.length === 0) {
461
- // skip generating empty clients
462
- continue;
463
- }
464
467
  // start with instantiable clients and recursively work down
465
468
  if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
466
469
  this.recursiveAdaptClient(client);
467
470
  }
468
471
  }
469
472
  }
473
+ /**
474
+ * formats input as a doc link.
475
+ * e.g. [`${id}`](${link})
476
+ * if doc links are disabled, id is returned
477
+ *
478
+ * @param id the ID of the doc link
479
+ * @param link the target of the doc link
480
+ * @returns the doc link or id
481
+ */
482
+ asDocLink(id, link) {
483
+ if (this.options['temp-omit-doc-links'] === true) {
484
+ return `\`${id}\``;
485
+ }
486
+ return `[\`${id}\`](${link})`;
487
+ }
470
488
  /**
471
489
  * recursively converts a client and its methods.
472
490
  * this simplifies the case for hierarchical clients.
@@ -502,15 +520,16 @@ export class Adapter {
502
520
  const rustClient = new rust.Client(clientName);
503
521
  rustClient.docs = this.adaptDocs(client.summary, client.doc);
504
522
  rustClient.parent = parent;
505
- rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'azure_core::http', 'Pipeline')));
523
+ rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'Pipeline', 'azure_core::http')));
506
524
  // anything other than public means non-instantiable client
507
525
  if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
508
526
  const clientOptionsStruct = new rust.Struct(`${rustClient.name}Options`, 'pub');
509
- const clientOptionsField = new rust.StructField('client_options', 'pub', new rust.ExternalType(this.crate, 'azure_core::http', 'ClientOptions'));
527
+ const clientOptionsField = new rust.StructField('client_options', 'pub', new rust.ExternalType(this.crate, 'ClientOptions', 'azure_core::http'));
528
+ clientOptionsField.docs.summary = 'Allows customization of the client.';
510
529
  clientOptionsField.defaultValue = 'ClientOptions::default()';
511
530
  clientOptionsStruct.fields.push(clientOptionsField);
512
531
  rustClient.constructable = new rust.ClientConstruction(new rust.ClientOptions(clientOptionsStruct));
513
- clientOptionsStruct.docs.summary = `Options used when creating a [\`${rustClient.name}\`](${rustClient.name})`;
532
+ clientOptionsStruct.docs.summary = `Options used when creating a ${this.asDocLink(rustClient.name, rustClient.name)}`;
514
533
  // NOTE: per tcgc convention, if there is no param of kind credential
515
534
  // it means that the client doesn't require any kind of authentication.
516
535
  // HOWEVER, if there *is* a credential param, then the client *does not*
@@ -674,14 +693,23 @@ export class Adapter {
674
693
  else {
675
694
  throw new Error(`uninstantiable client ${client.name} has no parent`);
676
695
  }
696
+ for (const child of values(client.children)) {
697
+ const subClient = this.recursiveAdaptClient(child, rustClient);
698
+ this.adaptClientAccessor(client, child, rustClient, subClient);
699
+ }
677
700
  for (const method of client.methods) {
678
- if (method.kind === 'clientaccessor') {
679
- const subClient = this.recursiveAdaptClient(method.response, rustClient);
680
- this.adaptClientAccessor(client, method, rustClient, subClient);
681
- }
682
- else {
683
- this.adaptMethod(method, rustClient);
701
+ if (method.kind === 'lro' || method.kind === 'lropaging') {
702
+ // skip LROs for now so that codegen is unblocked
703
+ // TODO: https://github.com/Azure/typespec-rust/issues/188
704
+ this.ctx.program.reportDiagnostic({
705
+ code: 'LroNotSupported',
706
+ severity: 'warning',
707
+ message: `skip emitting LRO ${method.name}`,
708
+ target: method.__raw ? method.__raw.node : NoTarget,
709
+ });
710
+ continue;
684
711
  }
712
+ this.adaptMethod(method, rustClient);
685
713
  }
686
714
  this.crate.clients.push(rustClient);
687
715
  return rustClient;
@@ -732,6 +760,7 @@ export class Adapter {
732
760
  if (param.optional || param.clientDefaultValue) {
733
761
  optional = true;
734
762
  const paramField = new rust.StructField(paramName, 'pub', paramType);
763
+ paramField.docs = this.adaptDocs(param.summary, param.doc);
735
764
  constructable.options.type.fields.push(paramField);
736
765
  if (param.clientDefaultValue) {
737
766
  paramField.defaultValue = `String::from("${param.clientDefaultValue}")`;
@@ -757,15 +786,15 @@ export class Adapter {
757
786
  * @param rustClient the client to which the method belongs
758
787
  * @param subClient the sub-client type that the method returns
759
788
  */
760
- adaptClientAccessor(client, method, rustClient, subClient) {
789
+ adaptClientAccessor(parentClient, childClient, rustClient, subClient) {
761
790
  const clientAccessor = new rust.ClientAccessor(`get_${snakeCaseName(subClient.name)}`, rustClient, subClient);
762
791
  clientAccessor.docs.summary = `Returns a new instance of ${subClient.name}.`;
763
- for (const param of method.parameters) {
792
+ for (const param of childClient.clientInitialization.parameters) {
764
793
  // check if the client's initializer already has this parameter.
765
794
  // if it does then omit it from the method sig as we'll populate
766
795
  // the child client's value from the parent.
767
796
  let existsOnParent = false;
768
- for (const clientParam of client.clientInitialization.parameters) {
797
+ for (const clientParam of parentClient.clientInitialization.parameters) {
769
798
  if (clientParam.name === param.name) {
770
799
  existsOnParent = true;
771
800
  break;
@@ -807,8 +836,8 @@ export class Adapter {
807
836
  const optionsLifetime = new rust.Lifetime('a');
808
837
  const methodOptionsStruct = new rust.Struct(`${rustClient.name}${codegen.pascalCase(srcMethodName)}Options`, 'pub');
809
838
  methodOptionsStruct.lifetime = optionsLifetime;
810
- methodOptionsStruct.docs.summary = `Options to be passed to [\`${rustClient.name}::${methodName}()\`](crate::generated::clients::${rustClient.name}::${methodName}())`;
811
- const clientMethodOptions = new rust.ExternalType(this.crate, 'azure_core::http', 'ClientMethodOptions');
839
+ methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
840
+ const clientMethodOptions = new rust.ExternalType(this.crate, 'ClientMethodOptions', 'azure_core::http');
812
841
  clientMethodOptions.lifetime = optionsLifetime;
813
842
  const methodOptionsField = new rust.StructField('method_options', 'pub', clientMethodOptions);
814
843
  methodOptionsField.docs.summary = 'Allows customization of the method call.';
@@ -834,18 +863,6 @@ export class Adapter {
834
863
  break;
835
864
  case 'paging':
836
865
  rustMethod = new rust.PageableMethod(methodName, rustClient, pub, methodOptions, httpMethod, httpPath);
837
- if (method.nextLinkOperation) {
838
- // TODO: https://github.com/Azure/autorest.rust/issues/103
839
- throw new Error('next page operation NYI');
840
- }
841
- else if (method.nextLinkPath) {
842
- // this is the name of the field in the response type that contains the next link URL
843
- if (method.nextLinkPath.indexOf('.') > 0) {
844
- // TODO: https://github.com/Azure/autorest.rust/issues/102
845
- throw new Error('nested next link path NYI');
846
- }
847
- rustMethod.strategy = new rust.PageableStrategyNextLink(naming.getEscapedReservedName(snakeCaseName(method.nextLinkPath), 'prop'));
848
- }
849
866
  break;
850
867
  default:
851
868
  throw new Error(`method kind ${method.kind} NYI`);
@@ -899,6 +916,13 @@ export class Adapter {
899
916
  fieldType = field.type;
900
917
  }
901
918
  else {
919
+ switch (adaptedParam.type.kind) {
920
+ case 'requestContent':
921
+ case 'struct':
922
+ break;
923
+ default:
924
+ adaptedParam.type = this.typeToWireType(adaptedParam.type);
925
+ }
902
926
  fieldType = new rust.Option(adaptedParam.type);
903
927
  }
904
928
  const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
@@ -931,10 +955,10 @@ export class Adapter {
931
955
  return this.adaptBodyFormat(defaultContentType);
932
956
  };
933
957
  // add any response headers
934
- const addedHeaders = new Set();
958
+ const responseHeaders = new Array();
935
959
  for (const httpResp of method.operation.responses) {
936
960
  for (const header of httpResp.headers) {
937
- if (addedHeaders.has(header.serializedName)) {
961
+ if (responseHeaders.find((e) => e.serializedName === header.serializedName)) {
938
962
  continue;
939
963
  }
940
964
  else if (header.type.kind === 'constant') {
@@ -947,19 +971,7 @@ export class Adapter {
947
971
  // response headers if they need it.
948
972
  continue;
949
973
  }
950
- let responseHeader;
951
- if (header.type.kind === 'dict') {
952
- if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
953
- throw new Error(`unexpected response header collection ${header.serializedName}`);
954
- }
955
- responseHeader = new rust.ResponseHeaderHashMap(header.name, header.serializedName);
956
- }
957
- else {
958
- responseHeader = new rust.ResponseHeaderScalar(fixETagName(header.name), fixETagName(header.serializedName), this.getType(header.type));
959
- }
960
- responseHeader.docs = this.adaptDocs(header.summary, header.doc);
961
- rustMethod.responseHeaders.push(responseHeader);
962
- addedHeaders.add(header.serializedName);
974
+ responseHeaders.push(header);
963
975
  }
964
976
  }
965
977
  let returnType;
@@ -978,16 +990,16 @@ export class Adapter {
978
990
  if (!this.crate.models.includes(synthesizedModel)) {
979
991
  this.crate.models.push(synthesizedModel);
980
992
  }
981
- returnType = new rust.Pager(this.crate, synthesizedModel, format);
993
+ returnType = new rust.Pager(this.crate, new rust.Payload(synthesizedModel, format));
982
994
  }
983
995
  else if (method.response.type && !(method.response.type.kind === 'bytes' && method.response.type.encode === 'bytes')) {
984
- returnType = new rust.Response(this.crate, new rust.Payload(this.getType(method.response.type), getBodyFormat()));
996
+ returnType = new rust.Response(this.crate, new rust.Payload(this.typeToWireType(this.getType(method.response.type)), getBodyFormat()));
985
997
  }
986
- else if (rustMethod.responseHeaders.length > 0) {
998
+ else if (responseHeaders.length > 0) {
987
999
  // for methods that don't return a modeled type but return headers,
988
1000
  // we need to return a marker type
989
1001
  const markerType = new rust.MarkerType(`${rustClient.name}${codegen.pascalCase(method.name)}Result`);
990
- markerType.docs.summary = `Contains results for [\`${rustClient.name}::${methodName}()\`](crate::generated::clients::${rustClient.name}::${methodName}())`;
1002
+ markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
991
1003
  returnType = new rust.Response(this.crate, markerType);
992
1004
  this.crate.models.push(markerType);
993
1005
  }
@@ -999,6 +1011,125 @@ export class Adapter {
999
1011
  returnType = new rust.Response(this.crate, this.getUnitType());
1000
1012
  }
1001
1013
  rustMethod.returns = new rust.Result(this.crate, returnType);
1014
+ rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, responseHeaders);
1015
+ if (method.kind === 'paging') {
1016
+ // can't do this until the method has been completely adapted
1017
+ rustMethod.strategy = this.adaptPageableMethodStrategy(method);
1018
+ }
1019
+ }
1020
+ /**
1021
+ * adapts response headers into a Rust ResponseHeadersTrait
1022
+ * if there are no response headers, undefined is returned.
1023
+ *
1024
+ * @param client the client that contains the method
1025
+ * @param method the method for which to create the trait
1026
+ * @param responseHeaders the response headers array (can be empty)
1027
+ * @returns a ResponseHeadersTrait or undefined
1028
+ */
1029
+ adaptResponseHeadersTrait(client, method, responseHeaders) {
1030
+ if (responseHeaders.length === 0) {
1031
+ return undefined;
1032
+ }
1033
+ // response header traits are only ever for marker types and payloads
1034
+ let implFor;
1035
+ switch (method.returns.type.kind) {
1036
+ case 'pager':
1037
+ implFor = method.returns.type.type;
1038
+ break;
1039
+ case 'response':
1040
+ 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}`);
1042
+ }
1043
+ implFor = method.returns.type.content;
1044
+ break;
1045
+ default:
1046
+ // this is Unit which should have been previously skipped
1047
+ throw new Error(`unexpected method return kind ${method.returns.type.kind}`);
1048
+ }
1049
+ let traitName;
1050
+ switch (implFor.kind) {
1051
+ case 'marker':
1052
+ traitName = implFor.name;
1053
+ break;
1054
+ case 'payload':
1055
+ traitName = this.recursiveTypeName(implFor.type);
1056
+ break;
1057
+ }
1058
+ traitName += 'Headers';
1059
+ // NOTE: the complete doc text will be emitted at codegen time
1060
+ const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
1061
+ 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
+ }
1077
+ return responseHeadersTrait;
1078
+ }
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
+ /**
1100
+ * creates the pageable strategy based on the method definition
1101
+ *
1102
+ * @param method the pageable method for which to create a strategy
1103
+ * @returns the pageable strategy
1104
+ */
1105
+ adaptPageableMethodStrategy(method) {
1106
+ if (method.pagingMetadata.nextLinkOperation) {
1107
+ // TODO: https://github.com/Azure/autorest.rust/issues/103
1108
+ throw new Error('next page operation NYI');
1109
+ }
1110
+ else if (method.pagingMetadata.nextLinkSegments) {
1111
+ if (method.pagingMetadata.nextLinkSegments.length > 1) {
1112
+ // TODO: https://github.com/Azure/autorest.rust/issues/102
1113
+ throw new Error('nested next link path NYI');
1114
+ }
1115
+ // this is the field in the response type that contains the next link URL
1116
+ const nextLinkSegment = method.pagingMetadata.nextLinkSegments[0];
1117
+ if (nextLinkSegment.kind !== 'property') {
1118
+ throw new Error(`unexpected kind ${nextLinkSegment.kind} for next link segment in operation ${method.name}`);
1119
+ }
1120
+ const nextLinkField = this.fieldsMap.get(nextLinkSegment);
1121
+ if (!nextLinkField) {
1122
+ // 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}`);
1124
+ }
1125
+ return new rust.PageableStrategyNextLink(nextLinkField);
1126
+ }
1127
+ else if (method.pagingMetadata.continuationTokenParameterSegments && method.pagingMetadata.continuationTokenResponseSegments) {
1128
+ throw new Error('PageableStrategyContinuationToken NYI');
1129
+ }
1130
+ else {
1131
+ throw new Error(`unhandled paging strategy for operation ${method.name}`);
1132
+ }
1002
1133
  }
1003
1134
  /**
1004
1135
  * converts a tcgc operation parameter into a Rust method parameter
@@ -1042,7 +1173,7 @@ export class Adapter {
1042
1173
  requestType = new rust.Bytes(this.crate);
1043
1174
  }
1044
1175
  else {
1045
- requestType = new rust.Payload(paramType, this.adaptBodyFormat(param.defaultContentType));
1176
+ requestType = new rust.Payload(this.typeToWireType(paramType), this.adaptBodyFormat(param.defaultContentType));
1046
1177
  }
1047
1178
  adaptedParam = new rust.BodyParameter(paramName, paramLoc, param.optional, new rust.RequestContent(this.crate, requestType));
1048
1179
  break;
@@ -1108,6 +1239,35 @@ export class Adapter {
1108
1239
  }
1109
1240
  return adaptedParam;
1110
1241
  }
1242
+ /**
1243
+ * narrows a rust.Type to a rust.WireType.
1244
+ * if type isn't a rust.WireType, an error is thrown.
1245
+ *
1246
+ * @param type the type to narrow
1247
+ * @returns the narrowed type
1248
+ */
1249
+ typeToWireType(type) {
1250
+ switch (type.kind) {
1251
+ case 'bytes':
1252
+ case 'encodedBytes':
1253
+ case 'enum':
1254
+ case 'enumValue':
1255
+ case 'Etag':
1256
+ case 'hashmap':
1257
+ case 'jsonValue':
1258
+ case 'literal':
1259
+ case 'model':
1260
+ case 'offsetDateTime':
1261
+ case 'scalar':
1262
+ case 'str':
1263
+ case 'String':
1264
+ case 'Url':
1265
+ case 'Vec':
1266
+ return type;
1267
+ default:
1268
+ throw new Error(`cannot convert ${type.kind} to a wire type`);
1269
+ }
1270
+ }
1111
1271
  /**
1112
1272
  * converts a tcgc spread parameter into a Rust partial body parameter.
1113
1273
  *