@azure-tools/typespec-rust 0.29.0 → 0.30.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 (53) hide show
  1. package/dist/src/codegen/clients.js +214 -97
  2. package/dist/src/codegen/clients.js.map +1 -1
  3. package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
  4. package/dist/src/codegen/codeGenerator.js +3 -1
  5. package/dist/src/codegen/codeGenerator.js.map +1 -1
  6. package/dist/src/codegen/context.d.ts +1 -1
  7. package/dist/src/codegen/context.d.ts.map +1 -1
  8. package/dist/src/codegen/context.js +1 -1
  9. package/dist/src/codegen/context.js.map +1 -1
  10. package/dist/src/codegen/enums.js +0 -1
  11. package/dist/src/codegen/enums.js.map +1 -1
  12. package/dist/src/codegen/helpers.d.ts +4 -0
  13. package/dist/src/codegen/helpers.d.ts.map +1 -1
  14. package/dist/src/codegen/helpers.js +23 -2
  15. package/dist/src/codegen/helpers.js.map +1 -1
  16. package/dist/src/codegen/mod.d.ts.map +1 -1
  17. package/dist/src/codegen/mod.js +4 -1
  18. package/dist/src/codegen/mod.js.map +1 -1
  19. package/dist/src/codegen/unions.d.ts +8 -1
  20. package/dist/src/codegen/unions.d.ts.map +1 -1
  21. package/dist/src/codegen/unions.js +42 -13
  22. package/dist/src/codegen/unions.js.map +1 -1
  23. package/dist/src/codegen/use.d.ts.map +1 -1
  24. package/dist/src/codegen/use.js +2 -1
  25. package/dist/src/codegen/use.js.map +1 -1
  26. package/dist/src/codemodel/client.d.ts +5 -0
  27. package/dist/src/codemodel/client.d.ts.map +1 -1
  28. package/dist/src/codemodel/client.js +1 -0
  29. package/dist/src/codemodel/client.js.map +1 -1
  30. package/dist/src/codemodel/crate.d.ts +1 -1
  31. package/dist/src/codemodel/crate.d.ts.map +1 -1
  32. package/dist/src/codemodel/crate.js +1 -1
  33. package/dist/src/codemodel/crate.js.map +1 -1
  34. package/dist/src/codemodel/types.d.ts +20 -22
  35. package/dist/src/codemodel/types.d.ts.map +1 -1
  36. package/dist/src/codemodel/types.js +8 -10
  37. package/dist/src/codemodel/types.js.map +1 -1
  38. package/dist/src/emitter.d.ts.map +1 -1
  39. package/dist/src/emitter.js +18 -15
  40. package/dist/src/emitter.js.map +1 -1
  41. package/dist/src/lib.d.ts +2 -0
  42. package/dist/src/lib.d.ts.map +1 -1
  43. package/dist/src/lib.js +6 -0
  44. package/dist/src/lib.js.map +1 -1
  45. package/dist/src/tcgcadapter/adapter.d.ts +5 -13
  46. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  47. package/dist/src/tcgcadapter/adapter.js +73 -88
  48. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  49. package/dist/src/tcgcadapter/naming.d.ts +2 -1
  50. package/dist/src/tcgcadapter/naming.d.ts.map +1 -1
  51. package/dist/src/tcgcadapter/naming.js +3 -2
  52. package/dist/src/tcgcadapter/naming.js.map +1 -1
  53. package/package.json +2 -2
@@ -10,6 +10,7 @@ import { CodegenError } from './errors.js';
10
10
  import * as helpers from './helpers.js';
11
11
  import queryString from 'query-string';
12
12
  import { Use } from './use.js';
13
+ import * as rust from '../codemodel/index.js';
13
14
  import * as shared from '../shared/shared.js';
14
15
  /**
15
16
  * emits the content for all client files
@@ -43,7 +44,7 @@ export function emitClients(crate) {
43
44
  body += `${indent.get()}${helpers.emitVisibility(field.visibility)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
44
45
  }
45
46
  body += '}\n\n'; // end client
46
- if (client.constructable) {
47
+ if (client.constructable && !client.constructable.suppressed) {
47
48
  // if client options doesn't require an impl for Default then just derive it
48
49
  let deriveDefault = 'Default, ';
49
50
  if (clientOptionsImplDefault(client.constructable)) {
@@ -67,7 +68,7 @@ export function emitClients(crate) {
67
68
  }
68
69
  }
69
70
  body += `impl ${client.name} {\n`;
70
- if (client.constructable) {
71
+ if (client.constructable && !client.constructable.suppressed) {
71
72
  // this is an instantiable client, so we need to emit client options and constructors
72
73
  use.add('azure_core', 'Result');
73
74
  for (let i = 0; i < client.constructable.constructors.length; ++i) {
@@ -188,7 +189,7 @@ export function emitClients(crate) {
188
189
  case 'lro':
189
190
  isPublicApi = true;
190
191
  methodBody = (indentation) => {
191
- return getLroMethodBody(indentation, use, client, method);
192
+ return getLroMethodBody(crate, indentation, use, client, method);
192
193
  };
193
194
  break;
194
195
  case 'clientaccessor':
@@ -657,14 +658,15 @@ function getMethodParamGroup(method) {
657
658
  * @param indent the indentation helper currently in scope
658
659
  * @param param the parameter to which the contents of setter apply
659
660
  * @param setter the callback that emits the code to read from a param var
660
- * @param inClosure indicates if the value is being read from within a closure (e.g. pageable methods)
661
+ * @param optionsPrefix Syntax to access the options structure, including the dot
661
662
  * @returns
662
663
  */
663
- function getParamValueHelper(indent, param, inClosure, setter) {
664
+ function getParamValueHelper(indent, param, setter, optionsPrefix = 'options.') {
664
665
  if (param.optional && param.type.kind !== 'literal') {
666
+ const asRef = nonCopyableType(param.type) || isEnumString(param.type) ? '.as_ref()' : '';
665
667
  // optional params are in the unwrapped options local var
666
668
  const op = indent.get() + helpers.buildIfBlock(indent, {
667
- condition: `let Some(${param.name}) = ${inClosure && nonCopyableType(param.type) ? '&' : ''}options.${param.name}`,
669
+ condition: `let Some(${param.name}) = ${param.location === 'client' ? 'self.' : optionsPrefix}${param.name}${asRef}`,
668
670
  body: setter,
669
671
  });
670
672
  return op + '\n';
@@ -777,7 +779,7 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
777
779
  }
778
780
  }
779
781
  if (pathParam.optional) {
780
- body += `${indent.get()}${pathVarName} = ${helpers.buildMatch(indent, `options.${pathParam.name}`, [{
782
+ body += `${indent.get()}${pathVarName} = ${helpers.buildMatch(indent, `options.${pathParam.name}${nonCopyableType(pathParam.type) ? '.as_ref()' : ''}`, [{
781
783
  pattern: `Some(${pathParam.name})`,
782
784
  body: (indent) => wrapSortedVec(`${indent.get()}${pathVarName}.replace("{${pathParam.segment}}", ${paramExpression})\n`),
783
785
  }, {
@@ -793,8 +795,15 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
793
795
  body += `${indent.get()}${urlVarName}.append_path(${path});\n`;
794
796
  }
795
797
  }
798
+ let hasQueryBuilder = false;
799
+ const getQueryBuilder = function () {
800
+ hasQueryBuilder = true;
801
+ use.add('azure_core::http', 'UrlExt');
802
+ return `${indent.get()}let mut query_builder = ${urlVarName}.query_builder();\n`;
803
+ };
796
804
  if (pathChunks.length === 2) {
797
- body += `${indent.get()}${urlVarName}.query_pairs_mut()`;
805
+ body += getQueryBuilder();
806
+ body += `${indent.get()}query_builder`;
798
807
  // set the query params that were in the path
799
808
  const qps = queryString.parse(pathChunks[1]);
800
809
  for (const qp of Object.keys(qps)) {
@@ -815,39 +824,47 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
815
824
  }
816
825
  body += ';\n';
817
826
  }
827
+ if (paramGroups.query.length > 0 && !hasQueryBuilder) {
828
+ body += getQueryBuilder();
829
+ }
818
830
  for (const queryParam of paramGroups.query) {
819
831
  if (queryParam.kind === 'queryCollection' && queryParam.format === 'multi') {
820
- body += getParamValueHelper(indent, queryParam, false, () => {
832
+ body += getParamValueHelper(indent, queryParam, () => {
821
833
  const valueVar = queryParam.name[0];
822
834
  let text = `${indent.get()}for ${valueVar} in ${queryParam.name}.iter() {\n`;
823
- text += `${indent.push().get()}${urlVarName}.query_pairs_mut().append_pair("${queryParam.key}", ${valueVar});\n`;
835
+ // if queryParam is a &[&str] then we'll need to deref the iterator
836
+ const deref = shared.asTypeOf(queryParam.type, 'str', 'ref', 'slice', 'ref') ? '*' : '';
837
+ text += `${indent.push().get()}query_builder.append_pair("${queryParam.key}", ${deref}${getHeaderPathQueryParamValue(use, queryParam, !queryParam.optional, false, valueVar)});\n`;
824
838
  text += `${indent.pop().get()}}\n`;
825
839
  return text;
826
840
  });
827
841
  }
828
842
  else if (queryParam.kind === 'queryHashMap') {
829
- body += getParamValueHelper(indent, queryParam, false, () => {
843
+ body += getParamValueHelper(indent, queryParam, () => {
830
844
  let text = `${indent.get()}{\n`;
831
845
  text += `${indent.push().get()}let mut ${queryParam.name}_vec = ${queryParam.name}.iter().collect::<Vec<_>>();\n`;
832
846
  text += `${indent.get()}${queryParam.name}_vec.sort_by_key(|p| p.0);\n`;
833
847
  if (queryParam.explode) {
834
848
  text += `${indent.get()}for (k, v) in ${queryParam.name}_vec.iter() {\n`;
835
- text += `${indent.push().get()}${urlVarName}.query_pairs_mut().append_pair(k, &v.to_string());\n`;
849
+ text += `${indent.push().get()}query_builder.append_pair(*k, v.to_string());\n`;
836
850
  text += `${indent.pop().get()}}\n`;
837
851
  }
838
852
  else {
839
- text += `${indent.get()}${urlVarName}.query_pairs_mut().append_pair("${queryParam.key}", ${queryParam.name}_vec.iter().map(|(k, v)| format!("{k},{v}")).collect::<Vec<String>>().join(",").as_str());\n`;
853
+ text += `${indent.get()}query_builder.set_pair("${queryParam.key}", ${queryParam.name}_vec.iter().map(|(k, v)| format!("{k},{v}")).collect::<Vec<String>>().join(","));\n`;
840
854
  }
841
855
  text += `${indent.pop().get()}}\n`;
842
856
  return text;
843
857
  });
844
858
  }
845
859
  else {
846
- body += getParamValueHelper(indent, queryParam, false, () => {
847
- return `${indent.get()}${urlVarName}.query_pairs_mut().append_pair("${queryParam.key}", ${getHeaderPathQueryParamValue(use, queryParam, !queryParam.optional, false)});\n`;
860
+ body += getParamValueHelper(indent, queryParam, () => {
861
+ return `${indent.get()}query_builder.set_pair("${queryParam.key}", ${getHeaderPathQueryParamValue(use, queryParam, !queryParam.optional, false)});\n`;
848
862
  });
849
863
  }
850
864
  }
865
+ if (hasQueryBuilder) {
866
+ body += `${indent.get()}query_builder.build();\n`;
867
+ }
851
868
  return body;
852
869
  }
853
870
  /**
@@ -858,9 +875,11 @@ function constructUrl(indent, use, method, paramGroups, urlVarName) {
858
875
  * @param method the method for which we're building the body
859
876
  * @param paramGroups the param groups for the provided method
860
877
  * @param inClosure indicates if the request is being constructed within a closure (e.g. pageable methods)
878
+ * @param requestVarName name for the request variable
879
+ * @param optionsPrefix Syntax to access the options structure, including the dot
861
880
  * @returns the code which sets HTTP headers for the request
862
881
  */
863
- function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestVarName) {
882
+ function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestVarName, optionsPrefix = 'options.') {
864
883
  var _a, _b, _c;
865
884
  let body = '';
866
885
  for (const headerParam of paramGroups.header) {
@@ -875,7 +894,7 @@ function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestV
875
894
  },
876
895
  {
877
896
  pattern: 'PagerState::Initial',
878
- body: (indent) => `${indent.get()}&options.${headerParam.name}\n`,
897
+ body: (indent) => `${indent.get()}&${optionsPrefix}${headerParam.name}\n`,
879
898
  }
880
899
  ]) + ';\n';
881
900
  body += indent.get() + helpers.buildIfBlock(indent, {
@@ -890,15 +909,15 @@ function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestV
890
909
  // this logic happens elsewhere so we skip it here.
891
910
  continue;
892
911
  }
893
- body += getParamValueHelper(indent, headerParam, inClosure, () => {
912
+ body += getParamValueHelper(indent, headerParam, () => {
894
913
  if (headerParam.kind === 'headerHashMap') {
895
- let setter = `for (k, v) in ${headerParam.type.kind === 'ref' ? '' : '&'}${headerParam.name} {\n`;
914
+ let setter = `for (k, v) in ${headerParam.name} {\n`;
896
915
  setter += `${indent.push().get()}${requestVarName}.insert_header(format!("${headerParam.header}-{k}"), v);\n`;
897
916
  setter += `${indent.pop().get()}}\n`;
898
917
  return setter;
899
918
  }
900
919
  return `${indent.get()}${requestVarName}.insert_header("${headerParam.header.toLowerCase()}", ${getHeaderPathQueryParamValue(use, headerParam, !inClosure, false)});\n`;
901
- });
920
+ }, optionsPrefix);
902
921
  }
903
922
  return body;
904
923
  }
@@ -936,7 +955,7 @@ function constructRequest(indent, use, method, paramGroups, inClosure, urlVarNam
936
955
  }
937
956
  const bodyParam = paramGroups.body;
938
957
  if (bodyParam) {
939
- body += getParamValueHelper(indent, bodyParam, inClosure, () => {
958
+ body += getParamValueHelper(indent, bodyParam, () => {
940
959
  let bodyParamContent = '';
941
960
  if (optionalContentTypeParam) {
942
961
  bodyParamContent = `${indent.get()}${requestVarName}.insert_header("${optionalContentTypeParam.header.toLowerCase()}", ${getHeaderPathQueryParamValue(use, optionalContentTypeParam, !inClosure, false)});\n`;
@@ -1117,21 +1136,14 @@ function getPageableMethodBody(indent, use, client, method) {
1117
1136
  // if the url already contains the token query param,
1118
1137
  // e.g. we started on some page, then we need to remove
1119
1138
  // it before appending the token for the next page.
1139
+ use.add('azure_core::http', 'UrlExt');
1120
1140
  const reqTokenValue = method.strategy.requestToken.key;
1121
1141
  body += `${indent.get()}${helpers.buildIfBlock(indent, {
1122
1142
  condition: `let PagerState::More(${reqTokenParam}) = ${reqTokenParam}`,
1123
1143
  body: (indent) => {
1124
- let body = indent.get() + helpers.buildIfBlock(indent, {
1125
- condition: `url.query_pairs().any(|(name, _)| name.eq("${reqTokenValue}"))`,
1126
- body: (indent) => {
1127
- let body = `${indent.get()}let mut new_url = url.clone();\n`;
1128
- body += `${indent.get()}new_url.query_pairs_mut().clear().extend_pairs(url.query_pairs().filter(|(name, _)| name.ne("${reqTokenValue}")));\n`;
1129
- body += `${indent.get()}url = new_url;\n`;
1130
- return body;
1131
- },
1132
- }) + '\n';
1133
- body += `${indent.get()}url.query_pairs_mut().append_pair("${reqTokenValue}", &${reqTokenParam});\n`;
1134
- return body;
1144
+ return `${indent.get()}let mut query_builder = url.query_builder();\n`
1145
+ + `${indent.get()}query_builder.set_pair("${reqTokenValue}", &${reqTokenParam});\n`
1146
+ + `${indent.get()}query_builder.build();\n`;
1135
1147
  }
1136
1148
  })}\n`;
1137
1149
  }
@@ -1147,26 +1159,32 @@ function getPageableMethodBody(indent, use, client, method) {
1147
1159
  body: (indent) => {
1148
1160
  const cloneNextLink = `${indent.get()}let mut ${nextLinkName} = ${nextLinkName}.clone();\n`;
1149
1161
  let content = '';
1162
+ let hasQueryBuilder = false;
1150
1163
  if (paramGroups.apiVersion && paramGroups.apiVersion.kind === 'queryScalar') {
1151
- const apiVersionKey = `"${paramGroups.apiVersion.key}"`;
1152
- // there are no APIs to set/update an existing query parameter.
1153
- // so, we filter the existing query params to remove the api-version
1154
- // query param. we then add back the filtered set and then add the
1155
- // api-version as specified on the client.
1156
- content = `${indent.get()}let qp = ${nextLinkName}.query_pairs().filter(|(name, _)| name.ne(${apiVersionKey}));\n`;
1157
1164
  content += cloneNextLink;
1158
- content += `${indent.get()}${nextLinkName}.query_pairs_mut().clear().extend_pairs(qp).append_pair(${apiVersionKey}, &${paramGroups.apiVersion.name});\n`;
1165
+ hasQueryBuilder = true;
1166
+ use.add('azure_core::http', 'UrlExt');
1167
+ content += `${indent.get()}let mut query_builder = ${nextLinkName}.query_builder();\n`;
1168
+ content += `${indent.get()}query_builder.set_pair("${paramGroups.apiVersion.key}", &${paramGroups.apiVersion.name});\n`;
1159
1169
  }
1160
1170
  else if (reinjectedParams.length > 0) {
1161
1171
  // if we didn't clone it above, we'll need to clone it for this case
1162
1172
  content += cloneNextLink;
1163
1173
  }
1164
1174
  // add query params for reinjection
1175
+ if (reinjectedParams.length > 0 && !hasQueryBuilder) {
1176
+ hasQueryBuilder = true;
1177
+ use.add('azure_core::http', 'UrlExt');
1178
+ content += `${indent.get()}let mut query_builder = ${nextLinkName}.query_builder();\n`;
1179
+ }
1165
1180
  for (const reinjectedParam of reinjectedParams) {
1166
- content += getParamValueHelper(indent, reinjectedParam, true, () => {
1167
- return `${indent.get()}${nextLinkName}.query_pairs_mut().append_pair("${reinjectedParam.key}", ${getHeaderPathQueryParamValue(use, reinjectedParam, false, false)});\n`;
1181
+ content += getParamValueHelper(indent, reinjectedParam, () => {
1182
+ return `${indent.get()}query_builder.set_pair("${reinjectedParam.key}", ${getHeaderPathQueryParamValue(use, reinjectedParam, false, false)});\n`;
1168
1183
  });
1169
1184
  }
1185
+ if (hasQueryBuilder) {
1186
+ content += `${indent.get()}query_builder.build();\n`;
1187
+ }
1170
1188
  content += `${indent.get()}${nextLinkName}\n`;
1171
1189
  return content;
1172
1190
  }
@@ -1284,18 +1302,22 @@ function getPageableMethodBody(indent, use, client, method) {
1284
1302
  /**
1285
1303
  * constructs the body for an LRO client method
1286
1304
  *
1305
+ * @param crate the crate to which method belongs
1287
1306
  * @param indent the indentation helper currently in scope
1288
1307
  * @param use the use statement builder currently in scope
1289
1308
  * @param client the client to which the method belongs
1290
1309
  * @param method the method for the body to build
1291
1310
  * @returns the contents of the method body
1292
1311
  */
1293
- function getLroMethodBody(indent, use, client, method) {
1294
- var _a, _b;
1295
- const pollingStepHeaderName = ((_a = method.responseHeaders) === null || _a === void 0 ? void 0 : _a.headers.some(h => h.header.toLowerCase() === 'operation-location'))
1296
- ? 'operation-location'
1297
- : ((_b = method.responseHeaders) === null || _b === void 0 ? void 0 : _b.headers.some(h => h.header.toLowerCase() === 'azure-asyncoperation'))
1298
- ? 'azure-asyncoperation' : undefined;
1312
+ function getLroMethodBody(crate, indent, use, client, method) {
1313
+ var _a;
1314
+ let pollingStepHeaderName = undefined;
1315
+ for (const header of ['operation-location', 'azure-asyncoperation', 'location']) {
1316
+ if ((_a = method.responseHeaders) === null || _a === void 0 ? void 0 : _a.headers.some(h => h.header.toLowerCase() === header)) {
1317
+ pollingStepHeaderName = header;
1318
+ break;
1319
+ }
1320
+ }
1299
1321
  const bodyFormat = helpers.convertResponseFormat(method.returns.type.type.format);
1300
1322
  use.add('azure_core::http', 'Method', 'RawResponse', 'Request', 'Url');
1301
1323
  use.add('azure_core::http::headers', 'RETRY_AFTER', 'X_MS_RETRY_AFTER_MS', 'RETRY_AFTER_MS');
@@ -1319,39 +1341,46 @@ function getLroMethodBody(indent, use, client, method) {
1319
1341
  const applyApiVersionParam = function (indent, paramGroups, newLinkVarName, existingLinkVarAccessor) {
1320
1342
  var _a;
1321
1343
  if (((_a = paramGroups.apiVersion) === null || _a === void 0 ? void 0 : _a.kind) === 'queryScalar') {
1322
- return `${indent.get()}let qp = ${existingLinkVarAccessor}.query_pairs().filter(|(name, _)| name.ne("${paramGroups.apiVersion.key}"));\n`
1323
- + `${indent.get()}let mut ${newLinkVarName} = ${existingLinkVarAccessor}.clone();\n`
1324
- + `${indent.get()}${newLinkVarName}.query_pairs_mut().clear().extend_pairs(qp).append_pair("${paramGroups.apiVersion.key}", &${paramGroups.apiVersion.name});\n`;
1344
+ use.add('azure_core::http', 'UrlExt');
1345
+ return `${indent.get()}let mut ${newLinkVarName} = ${existingLinkVarAccessor}.clone();\n`
1346
+ + `${indent.get()}let mut query_builder = ${newLinkVarName}.query_builder();\n`
1347
+ + `${indent.get()}query_builder.set_pair("${paramGroups.apiVersion.key}", &${paramGroups.apiVersion.name});\n`
1348
+ + `${indent.get()}query_builder.build();\n`;
1325
1349
  }
1326
1350
  return '';
1327
1351
  };
1328
- const declareRequest = function (indent, use, method, paramGroups, requestVarName, linkVarName, forceMut) {
1352
+ const declareRequest = function (indent, use, method, paramGroups, requestVarName, linkExpr, forceMut, optionsPrefix) {
1329
1353
  let mutRequest = '';
1330
1354
  // if the only header is optional Content-Type it will not be used
1331
1355
  // by applyHeaderParams() in this case so don't make request mutable
1332
1356
  if (forceMut || paramGroups.header.length > 1 || (paramGroups.header.length === 1 && !isOptionalContentTypeHeader(paramGroups.header[0]))) {
1333
1357
  mutRequest = 'mut ';
1334
1358
  }
1335
- return `${indent.get()}let ${mutRequest}${requestVarName} = Request::new(${linkVarName}.clone(), Method::Get);\n`
1336
- + applyHeaderParams(indent, use, method, paramGroups, true, requestVarName);
1359
+ return `${indent.get()}let ${mutRequest}${requestVarName} = Request::new(${linkExpr}, Method::Get);\n`
1360
+ + applyHeaderParams(indent, use, method, paramGroups, true, requestVarName, optionsPrefix);
1337
1361
  };
1338
1362
  let stateVarName = 'next_link';
1339
1363
  let stateVarType = 'Url';
1340
1364
  let progressVarName = 'next_link';
1341
1365
  let stateNextLinkAccessor = 'next_link';
1342
1366
  let pollerStateMoreReturn = 'next_link';
1343
- let pollerStateInitialReturn = 'url.clone()';
1367
+ let pollerStateInitialReturn = `${urlVar}.clone()`;
1344
1368
  let pollerStatusInProgressReturn = 'next_link';
1345
- if (method.finalResultStrategy.kind === 'originalUri') {
1346
- body += `${indent.get()}struct Progress{ next_link: Url, first_rsp: Option<RawResponse>, }`;
1369
+ if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName !== pollingStepHeaderName) {
1370
+ // A case when the initial response contains two headers - one (usually, 'azure-asyncoperation') to poll operation status
1371
+ // and another (usually, 'location') to collect the final result. Both values are URLs.
1372
+ // But only the initial response will have these headers, i.e. responses from 'azure-asyncoperation' URL won't have the 'location'.
1373
+ // Therefore, we need to preserve both URLs and pass them between poller states, up to the moment when the operation status
1374
+ // is Succeeded, so that when operation is Succeeded, the poller can send a request to 'location' to collect the result.
1375
+ body += `${indent.get()}struct Progress{ next_link: Url, final_link: Url, }`;
1347
1376
  body += `${indent.get()}impl AsRef<str> for Progress{ fn as_ref(&self) -> &str{ self.next_link.as_ref() }}\n`;
1348
1377
  stateVarName = 'state';
1349
1378
  stateVarType = 'Progress';
1350
1379
  progressVarName = 'progress';
1351
1380
  stateNextLinkAccessor = 'progress.next_link';
1352
- pollerStateMoreReturn = 'Progress{next_link, first_rsp: progress.first_rsp}';
1353
- pollerStateInitialReturn = 'Progress{next_link: url.clone(), first_rsp: None}';
1354
- pollerStatusInProgressReturn = 'Progress{next_link, first_rsp}';
1381
+ pollerStateMoreReturn = 'Progress{next_link, final_link: progress.final_link}';
1382
+ pollerStateInitialReturn = `Progress{next_link: ${urlVar}.clone(), final_link: ${urlVar}.clone()}`;
1383
+ pollerStatusInProgressReturn = 'Progress{next_link, final_link}';
1355
1384
  }
1356
1385
  body += `${indent.get()}Ok(${method.returns.type.name}::from_callback(\n`;
1357
1386
  body += `${indent.push().get()}move |${stateVarName}: PollerState<${stateVarType}>, poller_options| {\n`;
@@ -1359,7 +1388,7 @@ function getLroMethodBody(indent, use, client, method) {
1359
1388
  pattern: `PollerState::More(${progressVarName})`,
1360
1389
  body: (indent) => {
1361
1390
  return applyApiVersionParam(indent, paramGroups, 'next_link', stateNextLinkAccessor)
1362
- + declareRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName, 'next_link')
1391
+ + declareRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName, 'next_link.clone()')
1363
1392
  + `${indent.get()}(${initialRequestResult.requestVarName}, ${pollerStateMoreReturn})\n`;
1364
1393
  },
1365
1394
  }, {
@@ -1372,12 +1401,72 @@ function getLroMethodBody(indent, use, client, method) {
1372
1401
  }])};\n`;
1373
1402
  body += `${indent.get()}let ctx = poller_options.context.clone();\n`;
1374
1403
  body += `${indent.get()}let pipeline = pipeline.clone();\n`;
1404
+ if (method.finalResultStrategy.kind === 'originalUri') {
1405
+ body += `${indent.get()}let final_link = url.clone();\n`;
1406
+ for (const headerParam of paramGroups.header.filter(h => h.type.kind !== 'literal' && !isOptionalContentTypeHeader(h))) {
1407
+ if (headerParam.type.kind !== 'enum') {
1408
+ const optionsPrefix = headerParam.optional ? 'options.' : '';
1409
+ body += `${indent.get()}let ${headerParam.name} = ${optionsPrefix}${headerParam.name}.clone();\n`;
1410
+ }
1411
+ }
1412
+ }
1413
+ // Match an ARM PUT operation ("create or update"), which may have immediate 200 OK response with no headers and no poller status
1414
+ const isArmPutLro = bodyFormat === 'json'
1415
+ && method.httpMethod === 'put'
1416
+ && method.returns.type.resultType !== undefined
1417
+ && method.finalResultStrategy.kind === 'header'
1418
+ && method.finalResultStrategy.headerName === 'azure-asyncoperation'
1419
+ && pollingStepHeaderName === 'azure-asyncoperation'
1420
+ && method.statusCodes.some(sc => sc == 200)
1421
+ && method.statusCodes.some(sc => sc == 201);
1422
+ // Match an ARM PATCH operation ("update"), which may have immediate 200 OK response with no headers and no poller status
1423
+ const isArmPatchLro = bodyFormat === 'json'
1424
+ && method.httpMethod === 'patch'
1425
+ && method.returns.type.resultType !== undefined
1426
+ && method.finalResultStrategy.kind === 'header'
1427
+ && method.finalResultStrategy.headerName === 'location'
1428
+ && pollingStepHeaderName === 'location'
1429
+ && method.statusCodes.some(sc => sc == 200)
1430
+ && method.statusCodes.some(sc => sc == 202);
1431
+ // Match an ARM POST operation ("export"), which may have empty initial response
1432
+ const isArmPostLro = bodyFormat === 'json'
1433
+ && method.httpMethod === 'post'
1434
+ && method.returns.type.resultType !== undefined
1435
+ && method.finalResultStrategy.kind === 'header'
1436
+ && method.finalResultStrategy.headerName === 'location'
1437
+ && pollingStepHeaderName === 'azure-asyncoperation'
1438
+ && method.statusCodes.some(sc => sc == 200)
1439
+ && method.statusCodes.some(sc => sc == 202);
1440
+ // Match an ARM DELETE operation, which may have empty responses and only communicate via status codes
1441
+ const isArmDeleteLro = bodyFormat === 'json'
1442
+ && method.httpMethod === 'delete'
1443
+ && method.returns.type.resultType === undefined
1444
+ && method.finalResultStrategy.kind === 'header'
1445
+ && method.finalResultStrategy.headerName === 'location'
1446
+ && pollingStepHeaderName === 'location'
1447
+ && method.statusCodes.some(sc => sc == 200)
1448
+ && method.statusCodes.some(sc => sc == 202)
1449
+ && method.statusCodes.some(sc => sc == 204);
1450
+ if (isArmPutLro || isArmPatchLro) {
1451
+ body += 'let original_url = url.clone();\n';
1452
+ }
1375
1453
  body += `${indent.get()}async move {\n`;
1376
1454
  body += `${indent.push().get()}let rsp = pipeline.send(&ctx, &mut ${initialRequestResult.requestVarName}, ${getPipelineOptions(indent, use, method)}).await?;\n`;
1377
- body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
1455
+ const needsMutBody = isArmPutLro || isArmPatchLro || isArmPostLro || isArmDeleteLro;
1456
+ body += `${indent.get()}let (status, headers, ${needsMutBody ? 'mut' : ''} body) = rsp.deconstruct();\n`;
1457
+ if (isArmPostLro || isArmDeleteLro) {
1458
+ body += `${indent.get()}if body.is_empty() {\n`;
1459
+ let emptyBodyExpr = '"{}"';
1460
+ if (isArmDeleteLro) {
1461
+ use.add('azure_core::http', 'StatusCode');
1462
+ emptyBodyExpr = `if status == StatusCode::NoContent { "{\\"status\\":\\"Succeeded\\"}" } else { "{}" }`;
1463
+ }
1464
+ body += `${indent.push().get()}body = azure_core::http::response::ResponseBody::from_bytes(${emptyBodyExpr});\n`;
1465
+ body += `${indent.pop().get()}}\n`;
1466
+ }
1378
1467
  if (pollingStepHeaderName !== undefined) {
1379
1468
  body += `${indent.get()}let next_link = ${helpers.buildMatch(indent, `headers.get_optional_string(&HeaderName::from_static("${pollingStepHeaderName}"))`, [{
1380
- pattern: `Some(operation_location)`,
1469
+ pattern: 'Some(operation_location)',
1381
1470
  body: (indent) => {
1382
1471
  return `${indent.get()}Url::parse(&operation_location)?\n`;
1383
1472
  }
@@ -1388,32 +1477,48 @@ function getLroMethodBody(indent, use, client, method) {
1388
1477
  }
1389
1478
  }])};\n`;
1390
1479
  }
1391
- let resultRspVarName = 'first_rsp';
1392
- if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName !== pollingStepHeaderName) { // separate link for picking up the result
1393
- body += `${indent.get()}let final_link = Url::parse(headers.get_str(&HeaderName::from_static("${method.finalResultStrategy.headerName}"))?)?;\n`;
1480
+ if (isArmPutLro || isArmPatchLro) {
1481
+ use.add('azure_core::http', 'StatusCode');
1482
+ body += `${indent.get()}let mut final_body = None;\n`;
1483
+ body += `${indent.get()}if status == StatusCode::Ok && next_link.as_str() == original_url.as_str() {\n`;
1484
+ body += `${indent.push().get()}final_body = Some(body);\n`;
1485
+ body += `${indent.get()}body = azure_core::http::response::ResponseBody::from_bytes("{\\"status\\":\\"Succeeded\\"}");\n`;
1486
+ body += `${indent.pop().get()}}\n`;
1394
1487
  }
1395
- else if (method.finalResultStrategy.kind === 'originalUri') { // result available via original request immediately
1396
- resultRspVarName = 'first_rsp';
1397
- body += `${indent.get()}let mut ${resultRspVarName} = progress.first_rsp;\n`;
1398
- body += `${indent.get()}if first_rsp.is_none() { ${resultRspVarName} = Some(RawResponse::from_bytes(status, headers.clone(), body.clone())); }\n`;
1488
+ if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName !== pollingStepHeaderName) { // separate link for picking up the result
1489
+ body += `${indent.get()}let final_link = ${helpers.buildMatch(indent, `headers.get_optional_string(&HeaderName::from_static("${method.finalResultStrategy.headerName}"))`, [{
1490
+ pattern: 'Some(final_link)',
1491
+ body: (indent) => {
1492
+ return `${indent.get()}Url::parse(&final_link)?\n`;
1493
+ }
1494
+ }, {
1495
+ pattern: 'None',
1496
+ body: (indent) => {
1497
+ return `${indent.get()}progress.final_link`;
1498
+ }
1499
+ }])};\n`;
1399
1500
  }
1400
1501
  body += `${indent.get()}let retry_after = get_retry_after(&headers, &[X_MS_RETRY_AFTER_MS, RETRY_AFTER_MS, RETRY_AFTER], &poller_options);\n`;
1401
1502
  const deserialize = `${bodyFormat}::from_${bodyFormat}`;
1402
1503
  use.add('azure_core', bodyFormat);
1403
- body += `${indent.get()}let res: ${helpers.getTypeDeclaration(helpers.unwrapType(method.returns.type))} = ${deserialize}(&body)?;\n`;
1404
- if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName === pollingStepHeaderName) { // result available at the poll step link when finished
1405
- resultRspVarName = 'final_rsp';
1406
- body += `${indent.get()}let mut ${resultRspVarName} = None;\n`;
1407
- body += `${indent.get()}if res.status() == PollerStatus::Succeeded {\n`;
1408
- indent.push();
1504
+ body += `${indent.push().get()}let res: ${helpers.getTypeDeclaration(helpers.unwrapType(method.returns.type))} = ${deserialize}(&body)?;\n`;
1505
+ if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName === pollingStepHeaderName) {
1506
+ body += `${indent.get()}let mut final_rsp: Option<RawResponse> = None;\n`;
1507
+ body += `if res.status() == PollerStatus::Succeeded {\n`;
1409
1508
  let responseBodyExpr = 'body.clone()';
1410
- if (method.finalResultStrategy.propertyName !== undefined) {
1509
+ if (isArmPutLro || isArmPatchLro) {
1510
+ responseBodyExpr = 'if let Some(final_body) = final_body { final_body } else { body.clone() }';
1511
+ }
1512
+ else if (method.finalResultStrategy.propertyName !== undefined) {
1411
1513
  responseBodyExpr = 'body';
1412
- body += `${indent.get()}let ${responseBodyExpr} = azure_core::http::response::ResponseBody::from_bytes(`
1413
- + `serde_json::from_str::<azure_core::Value>(body.clone().into_string()?.as_str())?["${method.finalResultStrategy.propertyName}"].to_string());\n`;
1514
+ if (bodyFormat === 'json') {
1515
+ crate.addDependency(new rust.CrateDependency('serde_json'));
1516
+ body += `${indent.get()}let body = azure_core::http::response::ResponseBody::from_bytes(`
1517
+ + `serde_json::from_str::<azure_core::Value>(body.clone().into_string()?.as_str())?["${method.finalResultStrategy.propertyName}"].to_string());\n`;
1518
+ }
1414
1519
  }
1415
- body += `${indent.get()}${resultRspVarName} = Some(RawResponse::from_bytes(status, headers.clone(), ${responseBodyExpr}));\n`;
1416
- body += `${indent.pop().get()}}\n`;
1520
+ body += `${indent.get()}final_rsp = Some(RawResponse::from_bytes(status, headers.clone(), ${responseBodyExpr}));\n`;
1521
+ body += `}\n`;
1417
1522
  }
1418
1523
  body += `${indent.get()}let rsp = RawResponse::from_bytes(status, headers, body).into();\n`;
1419
1524
  const arms = [{
@@ -1426,19 +1531,24 @@ function getLroMethodBody(indent, use, client, method) {
1426
1531
  arms.push({
1427
1532
  pattern: `PollerStatus::Succeeded`,
1428
1533
  body: (indent) => {
1429
- return `{\n`
1534
+ let body = `{\n`
1430
1535
  + `${indent.push().get()}PollerResult::Succeeded {\n`
1431
1536
  + `${indent.push().get()}response: rsp,\n`
1432
1537
  + `${indent.get()}target: Box::new(move || {\n`
1433
- + `${indent.push().get()}Box::pin(async move {\n`
1434
- + ((method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName !== pollingStepHeaderName)
1435
- ? (declareRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName, 'final_link', true)
1436
- + `Ok(pipeline.send(&ctx, &mut ${initialRequestResult.requestVarName}, None).await?.into())\n`)
1437
- : `Ok(${resultRspVarName}.unwrap().into())\n`)
1438
- + `${indent.pop().get()}})\n`
1538
+ + `${indent.push().get()}Box::pin(async move {\n`;
1539
+ if (method.finalResultStrategy.kind === 'header' && method.finalResultStrategy.headerName === pollingStepHeaderName) {
1540
+ use.add('azure_core::error', 'Error', 'ErrorKind');
1541
+ body += `Ok(final_rsp.ok_or_else(|| { Error::new(ErrorKind::Other, "missing final response")})?.into())\n`;
1542
+ }
1543
+ else {
1544
+ body += declareRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName, 'final_link', true, '')
1545
+ + `Ok(pipeline.send(&ctx, &mut ${initialRequestResult.requestVarName}, None).await?.into())\n`;
1546
+ }
1547
+ body += `${indent.pop().get()}})\n`
1439
1548
  + `${indent.get()}}),\n`
1440
1549
  + `${indent.pop().get()}}`
1441
1550
  + `${indent.pop().get()}}`;
1551
+ return body;
1442
1552
  }
1443
1553
  });
1444
1554
  arms.push({
@@ -1494,9 +1604,10 @@ function getClientSupplementalEndpointParamValue(param) {
1494
1604
  * @param param the param for which to get the value
1495
1605
  * @param fromSelf applicable for client params. when true, the prefix "self." is included
1496
1606
  * @param neverBorrow indicates that the borrowing calculation should be skipped
1607
+ * @param overrideParamName optional value to use as the parameter name instead of param.name
1497
1608
  * @returns the code to use for the param's value
1498
1609
  */
1499
- function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow) {
1610
+ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow, overrideParamName) {
1500
1611
  let paramName = param.name;
1501
1612
  // when fromSelf is false we assume that there's a local with the same name.
1502
1613
  // e.g. in pageable methods where we need to clone the params so they can be
@@ -1504,6 +1615,9 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow) {
1504
1615
  if (param.location === 'client' && fromSelf) {
1505
1616
  paramName = 'self.' + paramName;
1506
1617
  }
1618
+ else if (overrideParamName) {
1619
+ paramName = overrideParamName;
1620
+ }
1507
1621
  const encodeBytes = function (type, param) {
1508
1622
  const encoding = helpers.getBytesEncodingMethod(type.encoding, 'encode', use);
1509
1623
  if (param) {
@@ -1531,13 +1645,11 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow) {
1531
1645
  let paramValue;
1532
1646
  // contains the result from calculating if the
1533
1647
  // param requires borrowing.
1534
- let mustBorrow = true;
1648
+ let mustBorrow = !helpers.isQueryParameter(param);
1535
1649
  const paramType = helpers.unwrapType(param.type);
1536
- if (param.kind === 'headerCollection' || param.kind === 'queryCollection') {
1537
- if (param.format === 'multi') {
1538
- throw new CodegenError('InternalError', 'multi should have been handled outside getHeaderPathQueryParamValue');
1539
- }
1540
- else if (paramType.kind === 'String' || paramType.kind === 'str') {
1650
+ // we want multi to hit the else case so the necessary conversions etc can happen
1651
+ if ((param.kind === 'headerCollection' || param.kind === 'queryCollection') && param.format !== 'multi') {
1652
+ if (paramType.kind === 'String' || paramType.kind === 'str') {
1541
1653
  paramValue = `${paramName}.join("${getCollectionDelimiter(param.format)}")`;
1542
1654
  }
1543
1655
  else {
@@ -1560,8 +1672,8 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow) {
1560
1672
  switch (paramType.kind) {
1561
1673
  case 'String':
1562
1674
  paramValue = paramName;
1563
- // if the param is on the client, or it's optional, then we must borrow
1564
- mustBorrow = (param.location === 'client' && fromSelf) || param.optional;
1675
+ // if the param is on the client, then we must borrow
1676
+ mustBorrow = param.location === 'client' && fromSelf;
1565
1677
  break;
1566
1678
  case 'str':
1567
1679
  paramValue = paramName;
@@ -1576,7 +1688,7 @@ function getHeaderPathQueryParamValue(use, param, fromSelf, neverBorrow) {
1576
1688
  break;
1577
1689
  case 'enum':
1578
1690
  case 'scalar':
1579
- if (paramType.kind === 'enum' && (param.kind === 'pathScalar' || param.kind === 'queryScalar') && paramType.type === 'String') {
1691
+ if (isEnumString(paramType) && (helpers.isPathParameter(param) || helpers.isQueryParameter(param))) {
1580
1692
  // append_pair and path.replace() want a reference to the string
1581
1693
  paramValue = `${paramName}.as_ref()`;
1582
1694
  // as_ref() elides the need to borrow
@@ -1676,4 +1788,9 @@ function getPipelineOptions(indent, use, method) {
1676
1788
  return 'None';
1677
1789
  }
1678
1790
  }
1791
+ /** narrows type to an Enum type IFF its underlying type is String within the conditional block */
1792
+ function isEnumString(type) {
1793
+ const unwrapped = helpers.unwrapType(type);
1794
+ return unwrapped.kind === 'enum' && unwrapped.type === 'String';
1795
+ }
1679
1796
  //# sourceMappingURL=clients.js.map