@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
@@ -1 +1 @@
1
- {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,yBAAyB;AACzB,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,aAAa,GAAG,SAAS,CAoPxE"}
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,yBAAyB;AACzB,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,aAAa,GAAG,SAAS,CAoPxE"}
@@ -4,6 +4,7 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as codegen from '@azure-tools/codegen';
6
6
  import { values } from '@azure-tools/linq';
7
+ import { CodegenError } from './errors.js';
7
8
  import * as helpers from './helpers.js';
8
9
  import queryString from 'query-string';
9
10
  import { Use } from './use.js';
@@ -112,7 +113,7 @@ export function emitClients(crate) {
112
113
  continue;
113
114
  }
114
115
  if (!client.fields.find((v) => { return v.name === param.name; })) {
115
- throw new Error(`didn't find field in client ${client.name} for param ${param.name}`);
116
+ throw new CodegenError('InternalError', `didn't find field in client ${client.name} for param ${param.name}`);
116
117
  }
117
118
  // by convention, the param field and param name are the
118
119
  // same so we can use shorthand initialization syntax
@@ -124,10 +125,10 @@ export function emitClients(crate) {
124
125
  continue;
125
126
  }
126
127
  if (!client.fields.find((v) => { return v.name === param.name; })) {
127
- throw new Error(`didn't find field in client ${client.name} for param ${param.name}`);
128
+ throw new CodegenError('InternalError', `didn't find field in client ${client.name} for param ${param.name}`);
128
129
  }
129
130
  if (!client.constructable.options.type.fields.find((v) => { return v.name === param.name; })) {
130
- throw new Error(`didn't find field in client options ${client.constructable.options.type.name} for optional param ${param.name}`);
131
+ throw new CodegenError('InternalError', `didn't find field in client options ${client.constructable.options.type.name} for optional param ${param.name}`);
131
132
  }
132
133
  body += `${indent.get()}${param.name}: options.${param.name},\n`;
133
134
  }
@@ -349,11 +350,7 @@ function getConstructorParamsSig(params, options, use) {
349
350
  continue;
350
351
  }
351
352
  use.addForType(param.type);
352
- let ref = '';
353
- if (param.kind === 'clientMethod' && param.ref) {
354
- ref = '&';
355
- }
356
- paramsSig.push(`${param.name}: ${ref}${helpers.getTypeDeclaration(param.type)}`);
353
+ paramsSig.push(`${param.name}: ${helpers.getTypeDeclaration(param.type)}`);
357
354
  }
358
355
  paramsSig.push(`options: ${helpers.getTypeDeclaration(options)}`);
359
356
  return paramsSig.join(', ');
@@ -379,13 +376,14 @@ function getMethodParamsSig(method, use) {
379
376
  }
380
377
  else {
381
378
  for (const param of method.params) {
382
- if (param.type.kind === 'literal') {
379
+ const paramType = helpers.unwrapType(param.type);
380
+ if (paramType.kind === 'literal') {
383
381
  // literal params are embedded directly in the code (e.g. accept header param)
384
382
  continue;
385
383
  }
386
- else if (param.type.kind === 'enumValue') {
384
+ else if (paramType.kind === 'enumValue') {
387
385
  // enum values are treated like literals, we just need to use their type
388
- use.addForType(param.type.type);
386
+ use.addForType(paramType.type);
389
387
  continue;
390
388
  }
391
389
  // don't add client or optional params to the method param sig
@@ -428,7 +426,7 @@ function getAuthPolicy(ctor, use) {
428
426
  */
429
427
  function formatParamTypeName(param) {
430
428
  let format = '';
431
- if (param.ref) {
429
+ if (param.ref === true) {
432
430
  format = '&';
433
431
  }
434
432
  if (param.mut) {
@@ -462,13 +460,13 @@ function getEndpointFieldName(client) {
462
460
  for (const field of client.fields) {
463
461
  if (field.type.kind === 'Url') {
464
462
  if (endpointFieldName) {
465
- throw new Error(`found multiple URL fields in client ${client.name} which is unexpected`);
463
+ throw new CodegenError('InternalError', `found multiple URL fields in client ${client.name} which is unexpected`);
466
464
  }
467
465
  endpointFieldName = field.name;
468
466
  }
469
467
  }
470
468
  if (!endpointFieldName) {
471
- throw new Error(`didn't find URI field for client ${client.name}`);
469
+ throw new CodegenError('InternalError', `didn't find URI field for client ${client.name}`);
472
470
  }
473
471
  return endpointFieldName;
474
472
  }
@@ -533,7 +531,7 @@ function getMethodParamGroup(method) {
533
531
  queryParams.push(param);
534
532
  break;
535
533
  }
536
- if (param.kind === 'query' && param.isApiVersion) {
534
+ if ((param.kind === 'header' || param.kind === 'query') && param.isApiVersion) {
537
535
  apiVersionParam = param;
538
536
  }
539
537
  }
@@ -544,7 +542,7 @@ function getMethodParamGroup(method) {
544
542
  for (const param of method.params) {
545
543
  if (param.kind === 'body') {
546
544
  if (bodyParam) {
547
- throw new Error(`method ${method.name} has multiple body parameters`);
545
+ throw new CodegenError('InternalError', `method ${method.name} has multiple body parameters`);
548
546
  }
549
547
  bodyParam = param;
550
548
  }
@@ -568,13 +566,14 @@ function getMethodParamGroup(method) {
568
566
  * @param indent the indentation helper currently in scope
569
567
  * @param param the parameter to which the contents of setter apply
570
568
  * @param setter the callback that emits the code to read from a param var
569
+ * @param inClosure indicates if the value is being read from within a closure (e.g. pageable methods)
571
570
  * @returns
572
571
  */
573
- function getParamValueHelper(indent, param, setter) {
572
+ function getParamValueHelper(indent, param, inClosure, setter) {
574
573
  if (param.optional) {
575
574
  // optional params are in the unwrapped options local var
576
575
  const op = indent.get() + helpers.buildIfBlock(indent, {
577
- condition: `let Some(${param.name}) = options.${param.name}`,
576
+ condition: `let Some(${param.name}) = ${inClosure ? '&' : ''}options.${param.name}`,
578
577
  body: setter,
579
578
  });
580
579
  return op + '\n';
@@ -596,11 +595,14 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
596
595
  // including them in the call to set_path() causes the chars to be path-escaped.
597
596
  const pathChunks = method.httpPath.split('?');
598
597
  if (pathChunks.length > 2) {
599
- throw new Error('too many HTTP path chunks');
598
+ throw new CodegenError('InternalError', 'too many HTTP path chunks');
600
599
  }
601
600
  /** returns & if the param needs to be borrowed (which is the majority of cases), else the empty string */
602
601
  const borrowOrNot = function (param) {
603
- return param.ref ? '' : '&';
602
+ if (param.type.kind !== 'ref' || param.type.type.kind === 'encodedBytes' || param.type.type.kind === 'slice') {
603
+ return '&';
604
+ }
605
+ return '';
604
606
  };
605
607
  let body = '';
606
608
  // if path is just "/" no need to set it again, we're already there
@@ -649,7 +651,7 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
649
651
  }
650
652
  for (const queryParam of paramGroups.query) {
651
653
  if (queryParam.kind === 'queryCollection' && queryParam.format === 'multi') {
652
- body += getParamValueHelper(indent, queryParam, () => {
654
+ body += getParamValueHelper(indent, queryParam, false, () => {
653
655
  const valueVar = queryParam.name[0];
654
656
  let text = `${indent.get()}for ${valueVar} in ${queryParam.name}.iter() {\n`;
655
657
  text += `${indent.push().get()}${urlVarName}.query_pairs_mut().append_pair("${queryParam.key}", ${valueVar});\n`;
@@ -658,7 +660,7 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
658
660
  });
659
661
  }
660
662
  else {
661
- body += getParamValueHelper(indent, queryParam, () => {
663
+ body += getParamValueHelper(indent, queryParam, false, () => {
662
664
  // for enums we call .as_ref() which elides the need to borrow
663
665
  const borrow = queryParam.type.kind === 'enum' ? '' : borrowOrNot(queryParam);
664
666
  return `${indent.get()}${urlVarName}.query_pairs_mut().append_pair("${queryParam.key}", ${borrow}${getHeaderPathQueryParamValue(use, queryParam, !queryParam.optional)});\n`;
@@ -676,28 +678,40 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
676
678
  * @param use the use statement builder currently in scope
677
679
  * @param method the method for which we're building the body
678
680
  * @param paramGroups the param groups for the provided method
679
- * @param fromSelf applicable for client params. when true, the prefix "self." is included
681
+ * @param inClosure indicates if the request is being constructed within a closure (e.g. pageable methods)
680
682
  * @returns the request construction code
681
683
  */
682
- function constructRequest(indent, use, method, paramGroups, fromSelf = true) {
684
+ function constructRequest(indent, use, method, paramGroups, inClosure) {
685
+ var _a, _b, _c;
683
686
  let body = `${indent.get()}let mut request = Request::new(url, Method::${codegen.capitalize(method.httpMethod)});\n`;
684
687
  for (const headerParam of paramGroups.header) {
685
- body += getParamValueHelper(indent, headerParam, () => {
688
+ if (method.kind === 'pageable' && ((_a = method.strategy) === null || _a === void 0 ? void 0 : _a.kind) === 'continuationToken' && ((_b = method.strategy) === null || _b === void 0 ? void 0 : _b.requestToken.kind) === 'header' && ((_c = method.strategy) === null || _c === void 0 ? void 0 : _c.requestToken) === headerParam) {
689
+ // we have some special handling for the header continuation token.
690
+ // if we have a token value, i.e. from the next page, then use that value.
691
+ // if not, then check if an optional token value was provided.
692
+ body += indent.get() + helpers.buildIfBlock(indent, {
693
+ condition: `let Some(${headerParam.name}) = ${headerParam.name}.or_else(|| options.${headerParam.name}.clone())`,
694
+ body: (indent) => `${indent.get()}request.insert_header("${headerParam.header}", ${headerParam.name});\n`,
695
+ });
696
+ continue;
697
+ }
698
+ body += getParamValueHelper(indent, headerParam, inClosure, () => {
686
699
  if (headerParam.kind === 'headerHashMap') {
687
700
  let setter = `for (k, v) in &${headerParam.name} {\n`;
688
701
  setter += `${indent.push().get()}request.insert_header(format!("${headerParam.header}-{}", k), v);\n`;
689
702
  setter += `${indent.pop().get()}}\n`;
690
703
  return setter;
691
704
  }
692
- const borrow = headerParam.location === 'client' && nonCopyableType(headerParam.type) ? '&' : '';
693
- const toOwned = headerParam.ref ? '.to_owned()' : '';
694
- return `${indent.get()}request.insert_header("${headerParam.header.toLowerCase()}", ${borrow}${getHeaderPathQueryParamValue(use, headerParam, fromSelf)}${toOwned});\n`;
705
+ // for non-copyable params (e.g. String), we need to borrow them if they're on the
706
+ // client or we're in a closure and the param is required (header params are always owned)
707
+ const borrow = nonCopyableType(headerParam.type) && (headerParam.location === 'client' || (inClosure && !headerParam.optional)) ? '&' : '';
708
+ return `${indent.get()}request.insert_header("${headerParam.header.toLowerCase()}", ${borrow}${getHeaderPathQueryParamValue(use, headerParam, !inClosure)});\n`;
695
709
  });
696
710
  }
697
711
  const bodyParam = paramGroups.body;
698
712
  if (bodyParam) {
699
- body += getParamValueHelper(indent, bodyParam, () => {
700
- return `${indent.get()}request.set_body(${bodyParam.name});\n`;
713
+ body += getParamValueHelper(indent, bodyParam, inClosure, () => {
714
+ return `${indent.get()}request.set_body(${bodyParam.name}${inClosure ? '.clone()' : ''});\n`;
701
715
  });
702
716
  }
703
717
  else if (paramGroups.partialBody.length > 0) {
@@ -708,16 +722,13 @@ function constructRequest(indent, use, method, paramGroups, fromSelf = true) {
708
722
  indent.push();
709
723
  for (const partialBodyParam of paramGroups.partialBody) {
710
724
  if (partialBodyParam.type.content.type !== requestContentType.content.type) {
711
- throw new Error(`spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.type.name}, expected model type ${requestContentType.content.type.name}`);
725
+ throw new CodegenError('InternalError', `spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.type.name}, expected model type ${requestContentType.content.type.name}`);
712
726
  }
713
727
  if (partialBodyParam.optional) {
714
728
  body += `${indent.get()}${partialBodyParam.name}: options.${partialBodyParam.name},\n`;
715
729
  continue;
716
730
  }
717
731
  let initializer = partialBodyParam.name;
718
- if (partialBodyParam.ref) {
719
- initializer = `${initializer}.to_owned()`;
720
- }
721
732
  if (requestContentType.content.type.visibility === 'pub') {
722
733
  // spread param maps to a non-internal model, so it must be wrapped in Some()
723
734
  initializer = `Some(${initializer})`;
@@ -761,7 +772,7 @@ function getAsyncMethodBody(indent, use, client, method) {
761
772
  body += `${indent.get()}let ctx = Context::with_context(&options.method_options.context);\n`;
762
773
  body += `${indent.get()}let ${urlVarNeedsMut(paramGroups, method)}url = self.${getEndpointFieldName(client)}.clone();\n`;
763
774
  body += constructUrl(indent, use, method, paramGroups);
764
- body += constructRequest(indent, use, method, paramGroups);
775
+ body += constructRequest(indent, use, method, paramGroups, false);
765
776
  body += `${indent.get()}self.pipeline.send(&ctx, &mut request).await\n`;
766
777
  return body;
767
778
  }
@@ -776,14 +787,10 @@ function getAsyncMethodBody(indent, use, client, method) {
776
787
  */
777
788
  function getPageableMethodBody(indent, use, client, method) {
778
789
  if (!method.strategy) {
779
- throw new Error('paged method with no strategy NYI');
780
- }
781
- else if (method.strategy.kind !== 'nextLink') {
782
- throw new Error('paged methods other than nextLink NYI');
790
+ throw new CodegenError('InternalError', 'paged method with no strategy NYI');
783
791
  }
784
- const nextLinkName = method.strategy.nextLink.name;
785
792
  use.add('azure_core::http', 'Method', 'Pager', 'PagerResult', 'Request', 'Response', 'Url');
786
- use.add('azure_core', 'json', 'Result');
793
+ use.add('azure_core', method.returns.type.type.format, 'Result');
787
794
  use.addForType(method.returns.type.type.type);
788
795
  const paramGroups = getMethodParamGroup(method);
789
796
  const urlVar = 'first_url';
@@ -794,49 +801,114 @@ function getPageableMethodBody(indent, use, client, method) {
794
801
  if (paramGroups.apiVersion) {
795
802
  body += `${indent.get()}let ${paramGroups.apiVersion.name} = ${getHeaderPathQueryParamValue(use, paramGroups.apiVersion, true)}.clone();\n`;
796
803
  }
797
- body += `${indent.get()}Ok(Pager::from_callback(move |${nextLinkName}: Option<Url>| {\n`;
798
- body += `${indent.push().get()}let url = ` + helpers.buildMatch(indent, nextLinkName, [{
799
- pattern: `Some(${nextLinkName})`,
800
- body: (indent) => {
801
- if (paramGroups.apiVersion) {
802
- const apiVersionKey = `"${paramGroups.apiVersion.key}"`;
803
- // there are no APIs to set/update an existing query parameter.
804
- // so, we filter the existing query params to remove the api-version
805
- // query param. we then add back the filtered set and then add the
806
- // api-version as specified on the client.
807
- let setApiVerBody = `${indent.get()}let qp = ${nextLinkName}.query_pairs().filter(|(name, _)| name.ne(${apiVersionKey}));\n`;
808
- setApiVerBody += `${indent.get()}let mut ${nextLinkName} = ${nextLinkName}.clone();\n`;
809
- setApiVerBody += `${indent.get()}${nextLinkName}.query_pairs_mut().clear().extend_pairs(qp).append_pair(${apiVersionKey}, &${paramGroups.apiVersion.name});\n`;
810
- setApiVerBody += `${indent.get()}${nextLinkName}\n`;
811
- return setApiVerBody;
812
- }
813
- return `${indent.get()} ${nextLinkName}\n`;
804
+ switch (method.strategy.kind) {
805
+ case 'continuationToken': {
806
+ const reqTokenParam = method.strategy.requestToken.name;
807
+ body += `${indent.get()}Ok(Pager::from_callback(move |${reqTokenParam}: Option<String>| {\n`;
808
+ body += `${indent.push().get()}let ${method.strategy.requestToken.kind === 'query' ? 'mut ' : ''}url = first_url.clone();\n`;
809
+ if (method.strategy.requestToken.kind === 'query') {
810
+ // if the url already contains the token query param,
811
+ // e.g. we started on some page, then we need to remove
812
+ // it before appending the token for the next page.
813
+ const reqTokenValue = method.strategy.requestToken.key;
814
+ body += `${indent.get()}${helpers.buildIfBlock(indent, {
815
+ condition: `let Some(${reqTokenParam}) = ${reqTokenParam}`,
816
+ body: (indent) => {
817
+ let body = indent.get() + helpers.buildIfBlock(indent, {
818
+ condition: `url.query_pairs().any(|(name, _)| name.eq("${reqTokenValue}"))`,
819
+ body: (indent) => {
820
+ let body = `${indent.get()}let mut new_url = url.clone();\n`;
821
+ body += `${indent.get()}new_url.query_pairs_mut().clear().extend_pairs(url.query_pairs().filter(|(name, _)| name.ne("${reqTokenValue}")));\n`;
822
+ body += `${indent.get()}url = new_url;\n`;
823
+ return body;
824
+ },
825
+ });
826
+ body += `${indent.get()}url.query_pairs_mut().append_pair("${reqTokenValue}", &${reqTokenParam});\n`;
827
+ return body;
828
+ }
829
+ })}`;
814
830
  }
815
- }, {
816
- pattern: 'None',
817
- body: (indent) => `${indent.get()}${urlVar}.clone()\n`
818
- }]);
819
- body += ';\n';
831
+ break;
832
+ }
833
+ case 'nextLink': {
834
+ const nextLinkName = method.strategy.nextLink.name;
835
+ body += `${indent.get()}Ok(Pager::from_callback(move |${nextLinkName}: Option<Url>| {\n`;
836
+ body += `${indent.push().get()}let url = ` + helpers.buildMatch(indent, nextLinkName, [{
837
+ pattern: `Some(${nextLinkName})`,
838
+ body: (indent) => {
839
+ if (paramGroups.apiVersion && paramGroups.apiVersion.kind === 'query') {
840
+ const apiVersionKey = `"${paramGroups.apiVersion.key}"`;
841
+ // there are no APIs to set/update an existing query parameter.
842
+ // so, we filter the existing query params to remove the api-version
843
+ // query param. we then add back the filtered set and then add the
844
+ // api-version as specified on the client.
845
+ let setApiVerBody = `${indent.get()}let qp = ${nextLinkName}.query_pairs().filter(|(name, _)| name.ne(${apiVersionKey}));\n`;
846
+ setApiVerBody += `${indent.get()}let mut ${nextLinkName} = ${nextLinkName}.clone();\n`;
847
+ setApiVerBody += `${indent.get()}${nextLinkName}.query_pairs_mut().clear().extend_pairs(qp).append_pair(${apiVersionKey}, &${paramGroups.apiVersion.name});\n`;
848
+ setApiVerBody += `${indent.get()}${nextLinkName}\n`;
849
+ return setApiVerBody;
850
+ }
851
+ return `${indent.get()} ${nextLinkName}\n`;
852
+ }
853
+ }, {
854
+ pattern: 'None',
855
+ body: (indent) => `${indent.get()}${urlVar}.clone()\n`
856
+ }]);
857
+ body += ';\n';
858
+ break;
859
+ }
860
+ }
820
861
  // here we want the T in Pager<T>
821
862
  const returnType = helpers.getTypeDeclaration(method.returns.type.type.type);
822
- body += constructRequest(indent, use, method, paramGroups, false);
863
+ body += constructRequest(indent, use, method, paramGroups, true);
823
864
  body += `${indent.get()}let ctx = options.method_options.context.clone();\n`;
824
865
  body += `${indent.get()}let pipeline = pipeline.clone();\n`;
825
866
  body += `${indent.get()}async move {\n`;
826
867
  body += `${indent.push().get()}let rsp: Response<${returnType}> = pipeline.send(&ctx, &mut request).await?;\n`;
827
- body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
828
- body += `${indent.get()}let bytes = body.collect().await?;\n`;
829
- body += `${indent.get()}let res: ${returnType} = json::from_json(bytes.clone())?;\n`;
830
- body += `${indent.get()}let rsp = Response::from_bytes(status, headers, bytes);\n`;
831
- body += `${indent.get()}Ok(${helpers.buildMatch(indent, `res.${nextLinkName}`, [{
832
- pattern: `Some(${nextLinkName})`,
833
- returns: 'PagerResult::Continue',
834
- body: (indent) => `${indent.get()}response: rsp,\n${indent.get()}continuation: ${nextLinkName}.parse()?,\n`
835
- }, {
836
- pattern: 'None',
837
- returns: 'PagerResult::Complete',
838
- body: () => `${indent.get()}response: rsp,\n`
839
- }])}`;
868
+ // check if we need to extract the next link field from the response model
869
+ if (method.strategy.kind === 'nextLink' || method.strategy.responseToken.kind === 'modelField') {
870
+ body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
871
+ body += `${indent.get()}let bytes = body.collect().await?;\n`;
872
+ const deserialize = method.returns.type.type.format === 'json' ? 'json::from_json' : 'xml::read_xml';
873
+ body += `${indent.get()}let res: ${returnType} = ${deserialize}(&bytes)?;\n`;
874
+ body += `${indent.get()}let rsp = Response::from_bytes(status, headers, bytes);\n`;
875
+ }
876
+ let srcNextPage;
877
+ let nextPageValue;
878
+ let continuation;
879
+ switch (method.strategy.kind) {
880
+ case 'continuationToken':
881
+ nextPageValue = method.strategy.responseToken.name;
882
+ continuation = nextPageValue;
883
+ switch (method.strategy.responseToken.kind) {
884
+ case 'modelField':
885
+ srcNextPage = `res.${nextPageValue}`;
886
+ break;
887
+ case 'responseHeaderScalar':
888
+ if (!method.responseHeaders) {
889
+ throw new CodegenError('InternalError', `missing response headers trait for method ${method.name}`);
890
+ }
891
+ use.addForType(method.responseHeaders);
892
+ srcNextPage = `rsp.${method.strategy.responseToken.name}()?`;
893
+ break;
894
+ }
895
+ break;
896
+ case 'nextLink':
897
+ nextPageValue = method.strategy.nextLink.name;
898
+ srcNextPage = `res.${nextPageValue}`;
899
+ continuation = `${nextPageValue}.parse()?`;
900
+ break;
901
+ }
902
+ // we need to handle the case where the next page value is the empty string,
903
+ // so checking strickly for None(theNextLink) is insufficient.
904
+ // the most common case for this is XML, e.g. an empty tag like <NextLink />
905
+ body += `${indent.get()}let ${nextPageValue} = ${srcNextPage}.unwrap_or_default();\n`;
906
+ body += `${indent.get()}Ok(${helpers.buildIfBlock(indent, {
907
+ condition: `${nextPageValue}.is_empty()`,
908
+ body: (indent) => `${indent.get()}PagerResult::Complete { response: rsp }\n`,
909
+ }, {
910
+ body: (indent) => `${indent.get()} PagerResult::Continue { response: rsp, continuation: ${continuation} }\n`,
911
+ })}`;
840
912
  body += ')\n'; // end Ok
841
913
  body += `${indent.pop().get()}}\n`; // end async move
842
914
  body += `${indent.pop().get()}}))`; // end Ok/Pager::from_callback
@@ -862,7 +934,7 @@ function getClientEndpointParamValue(param) {
862
934
  case 'scalar':
863
935
  return `&${paramName}.to_string()`;
864
936
  default:
865
- throw new Error(`unhandled ${param.kind} param type kind ${param.type.kind}`);
937
+ throw new CodegenError('InternalError', `unhandled ${param.kind} param type kind ${param.type.kind}`);
866
938
  }
867
939
  }
868
940
  /**
@@ -913,51 +985,52 @@ function getHeaderPathQueryParamValue(use, param, fromSelf) {
913
985
  return `${param}.unix_timestamp().to_string()`;
914
986
  }
915
987
  };
988
+ const paramType = helpers.unwrapType(param.type);
916
989
  if (param.kind === 'headerCollection' || param.kind === 'queryCollection') {
917
990
  if (param.format === 'multi') {
918
- throw new Error('multi should have been handled outside getHeaderPathQueryParamValue');
991
+ throw new CodegenError('InternalError', 'multi should have been handled outside getHeaderPathQueryParamValue');
919
992
  }
920
- else if (param.type.type.kind === 'String') {
993
+ else if (paramType.kind === 'String' || paramType.kind === 'str') {
921
994
  return `${paramName}.join("${getCollectionDelimiter(param.format)}")`;
922
995
  }
923
996
  // convert the items to strings
924
997
  let strConv;
925
- switch (param.type.type.kind) {
998
+ switch (paramType.kind) {
926
999
  case 'encodedBytes':
927
- strConv = encodeBytes(param.type.type);
1000
+ strConv = encodeBytes(paramType);
928
1001
  break;
929
1002
  case 'offsetDateTime':
930
- strConv = `|i| ${encodeDateTime(param.type.type, 'i')}`;
1003
+ strConv = `|i| ${encodeDateTime(paramType, 'i')}`;
931
1004
  break;
932
1005
  default:
933
1006
  strConv = '|i| i.to_string()';
934
1007
  }
935
1008
  return `${param.name}.iter().map(${strConv}).collect::<Vec<String>>().join("${getCollectionDelimiter(param.format)}")`;
936
1009
  }
937
- switch (param.type.kind) {
1010
+ switch (paramType.kind) {
938
1011
  case 'String':
939
1012
  case 'str':
940
1013
  return paramName;
1014
+ case 'decimal':
1015
+ return `${param.name}.to_string()`;
941
1016
  case 'encodedBytes':
942
- return encodeBytes(param.type, paramName);
1017
+ return encodeBytes(paramType, paramName);
943
1018
  case 'enum':
944
1019
  case 'scalar':
945
- if (param.type.kind === 'enum' && param.kind === 'query') {
1020
+ if (paramType.kind === 'enum' && param.kind === 'query') {
946
1021
  // append_pair wants a reference to the string
947
1022
  // TODO: https://github.com/Azure/typespec-rust/issues/25
948
1023
  return `${paramName}.as_ref()`;
949
1024
  }
950
1025
  return `${paramName}.to_string()`;
951
1026
  case 'enumValue':
952
- return `${param.type.type.name}::${param.type.name}.to_string()`;
953
- case 'implTrait':
954
- return `${paramName}.into()`;
1027
+ return `${paramType.type.name}::${paramType.name}.to_string()`;
955
1028
  case 'literal':
956
- return `"${param.type.value}"`;
1029
+ return `"${paramType.value}"`;
957
1030
  case 'offsetDateTime':
958
- return encodeDateTime(param.type, param.name);
1031
+ return encodeDateTime(paramType, param.name);
959
1032
  default:
960
- throw new Error(`unhandled ${param.kind} param type kind ${param.type.kind}`);
1033
+ throw new CodegenError('InternalError', `unhandled ${param.kind} param type kind ${paramType.kind}`);
961
1034
  }
962
1035
  }
963
1036
  /**