@azure-tools/typespec-rust 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/src/codegen/cargotoml.d.ts.map +1 -1
  2. package/dist/src/codegen/cargotoml.js +3 -1
  3. package/dist/src/codegen/cargotoml.js.map +1 -1
  4. package/dist/src/codegen/clients.d.ts.map +1 -1
  5. package/dist/src/codegen/clients.js +251 -33
  6. package/dist/src/codegen/clients.js.map +1 -1
  7. package/dist/src/codegen/context.d.ts +10 -0
  8. package/dist/src/codegen/context.d.ts.map +1 -1
  9. package/dist/src/codegen/context.js +51 -3
  10. package/dist/src/codegen/context.js.map +1 -1
  11. package/dist/src/codegen/docTests.d.ts +12 -0
  12. package/dist/src/codegen/docTests.d.ts.map +1 -0
  13. package/dist/src/codegen/docTests.js +46 -0
  14. package/dist/src/codegen/docTests.js.map +1 -0
  15. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  16. package/dist/src/codegen/headerTraits.js +5 -0
  17. package/dist/src/codegen/headerTraits.js.map +1 -1
  18. package/dist/src/codegen/helpers.d.ts +39 -4
  19. package/dist/src/codegen/helpers.d.ts.map +1 -1
  20. package/dist/src/codegen/helpers.js +70 -7
  21. package/dist/src/codegen/helpers.js.map +1 -1
  22. package/dist/src/codegen/lib.d.ts.map +1 -1
  23. package/dist/src/codegen/lib.js +2 -0
  24. package/dist/src/codegen/lib.js.map +1 -1
  25. package/dist/src/codegen/models.d.ts.map +1 -1
  26. package/dist/src/codegen/models.js +172 -9
  27. package/dist/src/codegen/models.js.map +1 -1
  28. package/dist/src/codegen/use.d.ts.map +1 -1
  29. package/dist/src/codegen/use.js +4 -0
  30. package/dist/src/codegen/use.js.map +1 -1
  31. package/dist/src/codemodel/client.d.ts +14 -1
  32. package/dist/src/codemodel/client.d.ts.map +1 -1
  33. package/dist/src/codemodel/client.js +8 -0
  34. package/dist/src/codemodel/client.js.map +1 -1
  35. package/dist/src/codemodel/crate.js.map +1 -1
  36. package/dist/src/codemodel/types.d.ts +40 -16
  37. package/dist/src/codemodel/types.d.ts.map +1 -1
  38. package/dist/src/codemodel/types.js +15 -1
  39. package/dist/src/codemodel/types.js.map +1 -1
  40. package/dist/src/shared/shared.d.ts +6 -0
  41. package/dist/src/shared/shared.d.ts.map +1 -1
  42. package/dist/src/shared/shared.js +15 -0
  43. package/dist/src/shared/shared.js.map +1 -1
  44. package/dist/src/tcgcadapter/adapter.d.ts +8 -3
  45. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  46. package/dist/src/tcgcadapter/adapter.js +131 -42
  47. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  48. package/package.json +28 -28
@@ -1 +1 @@
1
- {"version":3,"file":"cargotoml.d.ts","sourceRoot":"","sources":["../../../src/codegen/cargotoml.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAgBvD"}
1
+ {"version":3,"file":"cargotoml.d.ts","sourceRoot":"","sources":["../../../src/codegen/cargotoml.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAkBvD"}
@@ -14,7 +14,9 @@ export function emitCargoToml(crate) {
14
14
  content += 'edition.workspace = true\n';
15
15
  content += 'license.workspace = true\n';
16
16
  content += 'repository.workspace = true\n';
17
- content += 'rust-version.workspace = true\n';
17
+ content += 'rust-version.workspace = true\n\n';
18
+ content += '[features]\n';
19
+ content += 'default = ["azure_core/default"]\n';
18
20
  if (crate.dependencies.length > 0) {
19
21
  content += '\n[dependencies]\n';
20
22
  for (const dependency of crate.dependencies) {
@@ -1 +1 @@
1
- {"version":3,"file":"cargotoml.js","sourceRoot":"","sources":["../../../src/codegen/cargotoml.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAI/F;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,OAAO,GAAG,sBAAsB,KAAK,CAAC,IAAI,iBAAiB,KAAK,CAAC,OAAO,KAAK,CAAC;IAClF,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,+BAA+B,CAAC;IAC3C,OAAO,IAAI,iCAAiC,CAAC;IAC7C,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,oBAAoB,CAAC;QAChC,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC5C,qEAAqE;YACrE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACpI,OAAO,IAAI,GAAG,UAAU,CAAC,IAAI,wBAAwB,QAAQ,MAAM,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"cargotoml.js","sourceRoot":"","sources":["../../../src/codegen/cargotoml.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAI/F;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,OAAO,GAAG,sBAAsB,KAAK,CAAC,IAAI,iBAAiB,KAAK,CAAC,OAAO,KAAK,CAAC;IAClF,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,4BAA4B,CAAC;IACxC,OAAO,IAAI,+BAA+B,CAAC;IAC3C,OAAO,IAAI,mCAAmC,CAAC;IAC/C,OAAO,IAAI,cAAc,CAAC;IAC1B,OAAO,IAAI,oCAAoC,CAAC;IAChD,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,oBAAoB,CAAC;QAChC,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC5C,qEAAqE;YACrE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACpI,OAAO,IAAI,GAAG,UAAU,CAAC,IAAI,wBAAwB,QAAQ,MAAM,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAG9C,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,CAiQxE"}
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../src/codegen/clients.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAG9C,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,CA2QxE"}
@@ -5,6 +5,7 @@
5
5
  // cspell: ignore conv
6
6
  import * as codegen from '@azure-tools/codegen';
7
7
  import { values } from '@azure-tools/linq';
8
+ import { emitHeaderTraitDocExample } from './docTests.js';
8
9
  import { CodegenError } from './errors.js';
9
10
  import * as helpers from './helpers.js';
10
11
  import queryString from 'query-string';
@@ -76,7 +77,7 @@ export function emitClients(crate) {
76
77
  if (paramsDocs) {
77
78
  body += paramsDocs;
78
79
  }
79
- body += `${indent.get()}#[tracing::new("${crate.name}")]\n`;
80
+ body += `${indent.get()}#[tracing::new("${client.languageIndependentName}")]\n`;
80
81
  body += `${indent.get()}pub fn ${constructor.name}(${getConstructorParamsSig(constructor.params, client.constructable.options, use)}) -> Result<Self> {\n`;
81
82
  body += `${indent.get()}let options = options.unwrap_or_default();\n`;
82
83
  // by convention, the endpoint param is always the first ctor param
@@ -142,7 +143,7 @@ export function emitClients(crate) {
142
143
  body += `${indent.get()}option_env!("CARGO_PKG_VERSION"),\n`;
143
144
  body += `${indent.get()}options.client_options,\n`;
144
145
  body += `${indent.get()}Vec::default(),\n`;
145
- body += `${indent.get()}${authPolicy ? 'vec![auth_policy]' : 'Vec::default()'},\n`;
146
+ body += `${indent.get()}${authPolicy ? 'vec![auth_policy]' : 'Vec::default()'}, None,\n`;
146
147
  body += `${indent.pop().get()}),\n`; // end Pipeline::new
147
148
  body += `${indent.pop().get()}})\n`; // end Ok
148
149
  body += `${indent.pop().get()}}\n`; // end constructor
@@ -183,6 +184,12 @@ export function emitClients(crate) {
183
184
  return getPageableMethodBody(indentation, use, client, method);
184
185
  };
185
186
  break;
187
+ case 'lro':
188
+ isPublicApi = true;
189
+ methodBody = (indentation) => {
190
+ return getLroMethodBody(indentation, use, client, method);
191
+ };
192
+ break;
186
193
  case 'clientaccessor':
187
194
  isSubclientNew = true;
188
195
  methodBody = (indentation) => {
@@ -195,6 +202,10 @@ export function emitClients(crate) {
195
202
  if (paramsDocs) {
196
203
  body += paramsDocs;
197
204
  }
205
+ // client accessors will never have response headers
206
+ if (method.kind !== 'clientaccessor' && method.responseHeaders) {
207
+ body += getHeaderTraitDocComment(indent, crate, method);
208
+ }
198
209
  if (isPublicApi) {
199
210
  body += `${indent.get()}#[tracing::function("${method.languageIndependentName}")]\n`;
200
211
  }
@@ -275,7 +286,7 @@ function getMethodOptions(crate) {
275
286
  }
276
287
  }
277
288
  body += '}\n\n'; // end options
278
- if (method.kind === 'pageable') {
289
+ if (method.kind === 'pageable' || method.kind === 'lro') {
279
290
  body += `impl ${helpers.getTypeDeclaration(method.options.type, true)} {\n`;
280
291
  body += `${indent.get()}pub fn into_owned(self) -> ${method.options.type.name}<'static> {\n`;
281
292
  body += `${indent.push().get()}${method.options.type.name} {\n`;
@@ -316,7 +327,7 @@ function getMethodOptions(crate) {
316
327
  */
317
328
  function getParamsBlockDocComment(indent, callable) {
318
329
  const formatParamBullet = function (paramName) {
319
- return `* \`${paramName}\` - `;
330
+ return `* ${helpers.wrapInBackTicks(paramName)} - `;
320
331
  };
321
332
  let paramsContent = '';
322
333
  for (const param of callable.params) {
@@ -412,6 +423,36 @@ function getMethodParamsSig(method, use) {
412
423
  }
413
424
  return paramsSig.join(', ');
414
425
  }
426
+ /**
427
+ * returns documentation for header trait access if the method has response headers.
428
+ *
429
+ * @param indent the current indentation level
430
+ * @param crate the crate to which method belongs
431
+ * @param method the method for which to generate header trait documentation
432
+ * @returns the header trait documentation or empty string if not applicable
433
+ */
434
+ function getHeaderTraitDocComment(indent, crate, method) {
435
+ if (!method.responseHeaders) {
436
+ return '';
437
+ }
438
+ const traitName = method.responseHeaders.name;
439
+ let headerDocs = `${indent.get()}///\n`;
440
+ headerDocs += `${indent.get()}/// ## Response Headers\n`;
441
+ headerDocs += `${indent.get()}///\n`;
442
+ headerDocs += `${indent.get()}/// The returned [${helpers.wrapInBackTicks('Response')}](azure_core::http::Response) implements the [${helpers.wrapInBackTicks(traitName)}] trait, which provides\n`;
443
+ headerDocs += `${indent.get()}/// access to response headers. For example:\n`;
444
+ headerDocs += `${indent.get()}///\n`;
445
+ headerDocs += emitHeaderTraitDocExample(crate.name, method.responseHeaders, indent);
446
+ headerDocs += `${indent.get()}///\n`;
447
+ headerDocs += `${indent.get()}/// ### Available headers\n`;
448
+ // List all available headers
449
+ for (const header of method.responseHeaders.headers) {
450
+ headerDocs += `${indent.get()}/// * [${helpers.wrapInBackTicks(header.name)}()](crate::generated::models::${traitName}::${header.name}) - ${header.header}\n`;
451
+ }
452
+ headerDocs += `${indent.get()}///\n`;
453
+ headerDocs += `${indent.get()}/// [${helpers.wrapInBackTicks(traitName)}]: crate::generated::models::${traitName}\n`;
454
+ return headerDocs;
455
+ }
415
456
  /**
416
457
  * returns the auth policy instantiation code if the ctor contains a credential param.
417
458
  * the policy will be a local var named auth_policy.
@@ -593,7 +634,7 @@ function getMethodParamGroup(method) {
593
634
  * @returns
594
635
  */
595
636
  function getParamValueHelper(indent, param, inClosure, setter) {
596
- if (param.optional) {
637
+ if (param.optional && param.type.kind !== 'literal') {
597
638
  // optional params are in the unwrapped options local var
598
639
  const op = indent.get() + helpers.buildIfBlock(indent, {
599
640
  condition: `let Some(${param.name}) = ${inClosure ? '&' : ''}options.${param.name}`,
@@ -622,7 +663,10 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
622
663
  }
623
664
  /** returns & if the param needs to be borrowed (which is the majority of cases), else the empty string */
624
665
  const borrowOrNot = function (param) {
625
- if (param.type.kind !== 'ref' || param.type.type.kind === 'encodedBytes' || param.type.type.kind === 'slice') {
666
+ // for string-based enums we call .as_ref() which elides the need to borrow.
667
+ // for numeric-based enums the borrow will be necessary.
668
+ // TODO: https://github.com/Azure/typespec-rust/issues/25
669
+ if (param.type.kind !== 'enum' && (param.type.kind !== 'ref' || param.type.type.kind === 'encodedBytes' || param.type.type.kind === 'slice')) {
626
670
  return '&';
627
671
  }
628
672
  return '';
@@ -645,7 +689,7 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
645
689
  body += `${indent.get()}let mut path = String::from(${path});\n`;
646
690
  for (const pathParam of paramGroups.path) {
647
691
  let wrapSortedVec = (s) => s;
648
- let paramExpression = `${borrowOrNot(pathParam)}${getHeaderPathQueryParamValue(use, pathParam, true)}`;
692
+ let paramExpression = getHeaderPathQueryParamValue(use, pathParam, true);
649
693
  if (pathParam.kind === 'pathHashMap') {
650
694
  wrapSortedVec = (s) => `${indent.get()}{`
651
695
  + `${indent.push().get()}let mut ${pathParam.name}_vec = ${pathParam.name}.iter().collect::<Vec<_>>();\n`
@@ -706,9 +750,22 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
706
750
  case 'matrix':
707
751
  paramExpression = `&format!(";${pathParam.name}={${paramExpression}}")`;
708
752
  break;
753
+ default:
754
+ paramExpression = `${borrowOrNot(pathParam)}${paramExpression}`;
709
755
  }
710
756
  }
711
- body += wrapSortedVec(`${indent.get()}path = path.replace("{${pathParam.segment}}", ${paramExpression});\n`);
757
+ if (pathParam.optional) {
758
+ body += `${indent.get()}path = ${helpers.buildMatch(indent, `options.${pathParam.name}`, [{
759
+ pattern: `Some(${pathParam.name})`,
760
+ body: (indent) => wrapSortedVec(`${indent.get()}path.replace("{${pathParam.segment}}", ${paramExpression})\n`),
761
+ }, {
762
+ pattern: `None`,
763
+ body: (indent) => `${indent.get()}path.replace("{${pathParam.segment}}", "")\n`,
764
+ }])};\n`;
765
+ }
766
+ else {
767
+ body += wrapSortedVec(`${indent.get()}path = path.replace("{${pathParam.segment}}", ${paramExpression});\n`);
768
+ }
712
769
  }
713
770
  path = '&path';
714
771
  body += `${indent.get()}${urlVarName} = ${urlVarName}.join(${path})?;\n`;
@@ -774,27 +831,19 @@ function constructUrl(indent, use, method, paramGroups, urlVarName = 'url') {
774
831
  return body;
775
832
  }
776
833
  /**
777
- * emits the code for building the HTTP request.
778
- * assumes that there's a local var 'url' which is the Url.
779
- * creates a mutable local 'request' which is the Request instance.
834
+ * emits the code for setting HTTP headers in a request.
780
835
  *
781
836
  * @param indent the indentation helper currently in scope
782
837
  * @param use the use statement builder currently in scope
783
838
  * @param method the method for which we're building the body
784
839
  * @param paramGroups the param groups for the provided method
785
840
  * @param inClosure indicates if the request is being constructed within a closure (e.g. pageable methods)
786
- * @returns the request construction code
841
+ * @returns the code which sets HTTP headers for the request
787
842
  */
788
- function constructRequest(indent, use, method, paramGroups, inClosure) {
843
+ function applyHeaderParams(indent, use, method, paramGroups, inClosure) {
789
844
  var _a, _b, _c;
790
- let body = `${indent.get()}let mut request = Request::new(url, Method::${codegen.capitalize(method.httpMethod)});\n`;
791
- let optionalContentTypeParam;
845
+ let body = '';
792
846
  for (const headerParam of paramGroups.header) {
793
- // if the content-type header is optional, we need to emit it inside the "if let Some(body)" clause below.
794
- if (headerParam.kind === 'headerScalar' && headerParam.optional && headerParam.header.toLowerCase() === 'content-type') {
795
- optionalContentTypeParam = headerParam;
796
- continue;
797
- }
798
847
  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) === 'headerScalar' && ((_c = method.strategy) === null || _c === void 0 ? void 0 : _c.requestToken) === headerParam) {
799
848
  // we have some special handling for the header continuation token.
800
849
  // if we have a token value, i.e. from the next page, then use that value.
@@ -818,6 +867,32 @@ function constructRequest(indent, use, method, paramGroups, inClosure) {
818
867
  return `${indent.get()}request.insert_header("${headerParam.header.toLowerCase()}", ${borrow}${getHeaderPathQueryParamValue(use, headerParam, !inClosure)});\n`;
819
868
  });
820
869
  }
870
+ return body;
871
+ }
872
+ /**
873
+ * emits the code for building the HTTP request.
874
+ * assumes that there's a local var 'url' which is the Url.
875
+ * creates a mutable local 'request' which is the Request instance.
876
+ *
877
+ * @param indent the indentation helper currently in scope
878
+ * @param use the use statement builder currently in scope
879
+ * @param method the method for which we're building the body
880
+ * @param paramGroups the param groups for the provided method
881
+ * @param inClosure indicates if the request is being constructed within a closure (e.g. pageable methods)
882
+ * @param cloneUrl indicates if url should be cloned when it is passed to initialize request
883
+ * @param forceMut indicates whether request should get declared as 'mut' regardless of whether there are any headers to set
884
+ * @returns the request construction code
885
+ */
886
+ function constructRequest(indent, use, method, paramGroups, inClosure, cloneUrl = false, forceMut = true) {
887
+ let body = `${indent.get()}let ${(forceMut || paramGroups.header.length > 0) ? 'mut ' : ''}request = Request::new(url${cloneUrl ? '.clone()' : ''}, Method::${codegen.capitalize(method.httpMethod)});\n`;
888
+ body += applyHeaderParams(indent, use, method, paramGroups, inClosure);
889
+ let optionalContentTypeParam;
890
+ for (const headerParam of paramGroups.header) {
891
+ // if the content-type header is optional, we need to emit it inside the "if let Some(body)" clause below.
892
+ if (headerParam.kind === 'headerScalar' && headerParam.optional && headerParam.header.toLowerCase() === 'content-type') {
893
+ optionalContentTypeParam = headerParam;
894
+ }
895
+ }
821
896
  const bodyParam = paramGroups.body;
822
897
  if (bodyParam) {
823
898
  body += getParamValueHelper(indent, bodyParam, inClosure, () => {
@@ -867,12 +942,13 @@ function constructRequest(indent, use, method, paramGroups, inClosure) {
867
942
  */
868
943
  function errIfNotSuccessResponse(use, indent) {
869
944
  use.add('azure_core', 'Error', 'Result');
945
+ use.add('azure_core::http::headers', 'ERROR_CODE');
870
946
  use.add('azure_core::error', 'ErrorKind', 'HttpError');
871
947
  return helpers.buildIfBlock(indent, {
872
948
  condition: '!rsp.status().is_success()',
873
949
  body: (indent) => {
874
950
  let body = `${indent.get()}let status = rsp.status();\n`;
875
- body += `${indent.get()}let http_error = HttpError::new(rsp).await;\n`;
951
+ body += `${indent.get()}let http_error = HttpError::new(rsp, Some(ERROR_CODE)).await;\n`;
876
952
  body += `${indent.get()}let error_kind = ErrorKind::http_response(status, http_error.error_code().map(std::borrow::ToOwned::to_owned));\n`;
877
953
  body += `${indent.get()}return Err(Error::new(error_kind, http_error));\n`;
878
954
  return body;
@@ -891,6 +967,58 @@ function urlVarNeedsMut(paramGroups, method) {
891
967
  }
892
968
  return '';
893
969
  }
970
+ /**
971
+ * emits "if path_param is empty then error" checks for string method path parameters
972
+ *
973
+ * @param indent the indentation helper currently in scope
974
+ * @param params the path params to enumerate, can be empty
975
+ * @returns the empty path param checks or the empty string if there are no checks
976
+ */
977
+ function checkEmptyRequiredPathParams(indent, params) {
978
+ let checks = '';
979
+ for (const param of params) {
980
+ if (param.optional || param.location === 'client') {
981
+ continue;
982
+ }
983
+ checks += emitEmptyPathParamCheck(indent, param);
984
+ }
985
+ return checks;
986
+ }
987
+ /**
988
+ * emits the "if path_param is empty then error" check.
989
+ * this is only applicable when the path param's type can
990
+ * be empty (e.g. a string). for types that can't be empty
991
+ * the empty string is returned.
992
+ *
993
+ * @param indent the indentation helper currently in scope
994
+ * @param param the path param for which to emit the check
995
+ * @returns the check or the empty string
996
+ */
997
+ function emitEmptyPathParamCheck(indent, param) {
998
+ let toString = '';
999
+ const paramType = param.type.kind === 'ref' ? param.type.type : param.type;
1000
+ switch (paramType.kind) {
1001
+ case 'String':
1002
+ case 'str':
1003
+ // need to check these for zero length
1004
+ break;
1005
+ case 'enum':
1006
+ if (!paramType.extensible) {
1007
+ // fixed enums will always have a value
1008
+ return '';
1009
+ }
1010
+ // need to get the underlying string value
1011
+ toString = '.as_ref()';
1012
+ break;
1013
+ default:
1014
+ // no length to check so bail
1015
+ return '';
1016
+ }
1017
+ return helpers.buildIfBlock(indent, {
1018
+ condition: `${param.name}${toString}.is_empty()`,
1019
+ body: (indent) => `${indent.get()}return Err(azure_core::Error::message(azure_core::error::ErrorKind::Other, "parameter ${param.name} cannot be empty"));`,
1020
+ });
1021
+ }
894
1022
  /**
895
1023
  * constructs the body for an async client method
896
1024
  *
@@ -901,10 +1029,11 @@ function urlVarNeedsMut(paramGroups, method) {
901
1029
  * @returns the contents of the method body
902
1030
  */
903
1031
  function getAsyncMethodBody(indent, use, client, method) {
904
- use.add('azure_core::http', 'Context', 'Method', 'Request');
1032
+ use.add('azure_core::http', 'Method', 'Request');
905
1033
  const paramGroups = getMethodParamGroup(method);
906
- let body = 'let options = options.unwrap_or_default();\n';
907
- body += `${indent.get()}let ctx = Context::with_context(&options.method_options.context);\n`;
1034
+ let body = checkEmptyRequiredPathParams(indent, paramGroups.path);
1035
+ body += 'let options = options.unwrap_or_default();\n';
1036
+ body += `${indent.get()}let ctx = options.method_options.context.to_borrowed();\n`;
908
1037
  body += `${indent.get()}let ${urlVarNeedsMut(paramGroups, method)}url = self.${getEndpointFieldName(client)}.clone();\n`;
909
1038
  body += constructUrl(indent, use, method, paramGroups);
910
1039
  body += constructRequest(indent, use, method, paramGroups, false);
@@ -931,13 +1060,15 @@ function getPageableMethodBody(indent, use, client, method) {
931
1060
  throw new CodegenError('InternalError', 'paged method with no strategy NYI');
932
1061
  }
933
1062
  const bodyFormat = helpers.convertResponseFormat(method.returns.type.type.format);
934
- use.add('azure_core::http', 'Method', 'PagerResult', 'Request', 'Url');
1063
+ use.add('azure_core::http', 'Method', 'Request', 'Url');
1064
+ use.add('azure_core::http::pager', 'PagerResult', 'PagerState');
935
1065
  use.add('azure_core', bodyFormat, 'Result');
936
1066
  use.addForType(method.returns.type);
937
1067
  use.addForType(helpers.unwrapType(method.returns.type));
938
1068
  const paramGroups = getMethodParamGroup(method);
939
1069
  const urlVar = 'first_url';
940
- let body = 'let options = options.unwrap_or_default().into_owned();\n';
1070
+ let body = checkEmptyRequiredPathParams(indent, paramGroups.path);
1071
+ body += 'let options = options.unwrap_or_default().into_owned();\n';
941
1072
  body += `${indent.get()}let pipeline = self.pipeline.clone();\n`;
942
1073
  body += `${indent.get()}let ${urlVarNeedsMut(paramGroups, method)}${urlVar} = self.${getEndpointFieldName(client)}.clone();\n`;
943
1074
  body += constructUrl(indent, use, method, paramGroups, urlVar);
@@ -947,7 +1078,7 @@ function getPageableMethodBody(indent, use, client, method) {
947
1078
  switch (method.strategy.kind) {
948
1079
  case 'continuationToken': {
949
1080
  const reqTokenParam = method.strategy.requestToken.name;
950
- body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(move |${reqTokenParam}: Option<String>| {\n`;
1081
+ body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(move |${reqTokenParam}: PagerState<String>| {\n`;
951
1082
  body += `${indent.push().get()}let ${method.strategy.requestToken.kind === 'queryScalar' ? 'mut ' : ''}url = first_url.clone();\n`;
952
1083
  if (method.strategy.requestToken.kind === 'queryScalar') {
953
1084
  // if the url already contains the token query param,
@@ -955,7 +1086,7 @@ function getPageableMethodBody(indent, use, client, method) {
955
1086
  // it before appending the token for the next page.
956
1087
  const reqTokenValue = method.strategy.requestToken.key;
957
1088
  body += `${indent.get()}${helpers.buildIfBlock(indent, {
958
- condition: `let Some(${reqTokenParam}) = ${reqTokenParam}`,
1089
+ condition: `let PagerState::More(${reqTokenParam}) = ${reqTokenParam}`,
959
1090
  body: (indent) => {
960
1091
  let body = indent.get() + helpers.buildIfBlock(indent, {
961
1092
  condition: `url.query_pairs().any(|(name, _)| name.eq("${reqTokenValue}"))`,
@@ -975,9 +1106,9 @@ function getPageableMethodBody(indent, use, client, method) {
975
1106
  }
976
1107
  case 'nextLink': {
977
1108
  const nextLinkName = method.strategy.nextLink.name;
978
- body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(move |${nextLinkName}: Option<Url>| {\n`;
1109
+ body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(move |${nextLinkName}: PagerState<Url>| {\n`;
979
1110
  body += `${indent.push().get()}let url = ` + helpers.buildMatch(indent, nextLinkName, [{
980
- pattern: `Some(${nextLinkName})`,
1111
+ pattern: `PagerState::More(${nextLinkName})`,
981
1112
  body: (indent) => {
982
1113
  if (paramGroups.apiVersion && paramGroups.apiVersion.kind === 'queryScalar') {
983
1114
  const apiVersionKey = `"${paramGroups.apiVersion.key}"`;
@@ -994,7 +1125,7 @@ function getPageableMethodBody(indent, use, client, method) {
994
1125
  return `${indent.get()} ${nextLinkName}\n`;
995
1126
  }
996
1127
  }, {
997
- pattern: 'None',
1128
+ pattern: 'PagerState::Initial',
998
1129
  body: (indent) => `${indent.get()}${urlVar}.clone()\n`
999
1130
  }]);
1000
1131
  body += ';\n';
@@ -1081,6 +1212,93 @@ function getPageableMethodBody(indent, use, client, method) {
1081
1212
  body += `${indent.pop().get()}}))`; // end Ok/Pager::from_callback
1082
1213
  return body;
1083
1214
  }
1215
+ /**
1216
+ * constructs the body for an LRO client method
1217
+ *
1218
+ * @param indent the indentation helper currently in scope
1219
+ * @param use the use statement builder currently in scope
1220
+ * @param client the client to which the method belongs
1221
+ * @param method the method for the body to build
1222
+ * @returns the contents of the method body
1223
+ */
1224
+ function getLroMethodBody(indent, use, client, method) {
1225
+ const bodyFormat = helpers.convertResponseFormat(method.returns.type.type.format);
1226
+ use.add('azure_core::http', 'Method', 'RawResponse', 'Request', 'Url');
1227
+ use.add('azure_core::http::headers', 'RETRY_AFTER', 'X_MS_RETRY_AFTER_MS', 'RETRY_AFTER_MS');
1228
+ use.add('azure_core::http::poller', 'get_retry_after', 'PollerResult', 'PollerState', 'PollerStatus', 'StatusMonitor as _');
1229
+ use.addForType(method.returns.type);
1230
+ use.addForType(helpers.unwrapType(method.returns.type));
1231
+ const paramGroups = getMethodParamGroup(method);
1232
+ const urlVar = 'url';
1233
+ let body = 'let options = options.unwrap_or_default().into_owned();\n';
1234
+ body += `${indent.get()}let pipeline = self.pipeline.clone();\n`;
1235
+ body += `${indent.get()}let ${urlVarNeedsMut(paramGroups, method)}${urlVar} = self.${getEndpointFieldName(client)}.clone();\n`;
1236
+ body += `${constructUrl(indent, use, method, paramGroups, urlVar)}\n\n`;
1237
+ if (paramGroups.apiVersion) {
1238
+ body += `${indent.get()}let ${paramGroups.apiVersion.name} = ${getHeaderPathQueryParamValue(use, paramGroups.apiVersion, true)}.clone();\n\n`;
1239
+ }
1240
+ body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(\n`;
1241
+ body += `${indent.push().get()}move |next_link: PollerState<Url>| {\n`;
1242
+ body += `${indent.push().get()}let (mut request, next_link) = ${helpers.buildMatch(indent, 'next_link', [{
1243
+ pattern: `PollerState::More(next_link)`,
1244
+ body: (indent) => {
1245
+ let body = '';
1246
+ if (paramGroups.apiVersion) {
1247
+ body += `${indent.get()}let qp = next_link.query_pairs().filter(|(name, _)| name.ne("api-version"));\n`;
1248
+ }
1249
+ body += `${indent.get()}let mut next_link = next_link.clone();\n`;
1250
+ if (paramGroups.apiVersion) {
1251
+ body += `${indent.get()}next_link.query_pairs_mut().clear().extend_pairs(qp).append_pair("api-version", &${paramGroups.apiVersion.name});\n\n`;
1252
+ }
1253
+ body += `${indent.get()}let ${paramGroups.header.length > 0 ? 'mut ' : ''}request = Request::new(next_link.clone(), Method::Get);`;
1254
+ body += `\n${applyHeaderParams(indent, use, method, paramGroups, true)}\n\n`;
1255
+ body += `${indent.get()}(request, next_link)\n`;
1256
+ return body;
1257
+ },
1258
+ }, {
1259
+ pattern: 'PollerState::Initial',
1260
+ body: (indent) => {
1261
+ let body = `${constructRequest(indent, use, method, paramGroups, true, true, false)}\n`;
1262
+ body += `${indent.get()}(request, url.clone())\n`;
1263
+ return body;
1264
+ },
1265
+ }])};\n\n`;
1266
+ body += `${indent.get()}let ctx = options.method_options.context.clone();\n`;
1267
+ body += `${indent.get()}let pipeline = pipeline.clone();\n`;
1268
+ body += `${indent.get()}async move {\n`;
1269
+ body += `${indent.push().get()}let rsp: RawResponse = pipeline.send(&ctx, &mut request).await?;\n`;
1270
+ body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
1271
+ body += `${indent.get()}let retry_after = get_retry_after(&headers, &[X_MS_RETRY_AFTER_MS, RETRY_AFTER_MS, RETRY_AFTER], &options.poller_options);\n`;
1272
+ body += `${indent.get()}let bytes = body.collect().await?;\n`;
1273
+ let deserialize = '';
1274
+ if (bodyFormat === 'json') {
1275
+ use.add('azure_core', 'json');
1276
+ deserialize = 'json::from_json';
1277
+ }
1278
+ else {
1279
+ deserialize = 'xml::read_xml';
1280
+ }
1281
+ body += `${indent.get()}let res: ${helpers.getTypeDeclaration(helpers.unwrapType(method.returns.type))} = ${deserialize}(&bytes)?;\n`;
1282
+ body += `${indent.get()}let rsp = RawResponse::from_bytes(status, headers, bytes).into();\n`;
1283
+ body += `${indent.get()}Ok(${helpers.buildMatch(indent, 'res.status()', [{
1284
+ pattern: `PollerStatus::InProgress`,
1285
+ body: (indent) => {
1286
+ return `${indent.get()}response: rsp, retry_after, next: next_link\n`;
1287
+ },
1288
+ returns: 'PollerResult::InProgress'
1289
+ }, {
1290
+ pattern: '_',
1291
+ body: (indent) => {
1292
+ return `${indent.get()}response: rsp`;
1293
+ },
1294
+ returns: 'PollerResult::Done'
1295
+ }])})\n`;
1296
+ body += `${indent.pop().get()}}\n`; // end async move
1297
+ body += `${indent.pop().get()}},\n`; // end move
1298
+ body += `${indent.get()}None,\n`;
1299
+ body += `${indent.pop().get()}))`; // end Ok/Poller::from_callback
1300
+ return body;
1301
+ }
1084
1302
  /**
1085
1303
  * contains the code to use when populating a client endpoint parameter value
1086
1304
  * from a parameter of that type.
@@ -1179,8 +1397,8 @@ function getHeaderPathQueryParamValue(use, param, fromSelf) {
1179
1397
  return encodeBytes(paramType, paramName);
1180
1398
  case 'enum':
1181
1399
  case 'scalar':
1182
- if (paramType.kind === 'enum' && param.kind === 'queryScalar') {
1183
- // append_pair wants a reference to the string
1400
+ if (paramType.kind === 'enum' && (param.kind === 'pathScalar' || param.kind === 'queryScalar')) {
1401
+ // append_pair and path.replace() want a reference to the string
1184
1402
  // TODO: https://github.com/Azure/typespec-rust/issues/25
1185
1403
  return `${paramName}.as_ref()`;
1186
1404
  }