@azure-tools/typespec-rust 0.41.3 → 0.41.5

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.
@@ -1090,6 +1090,51 @@ function applyHeaderParams(indent, use, method, paramGroups, inClosure, requestV
1090
1090
  function isOptionalContentTypeHeader(headerParam) {
1091
1091
  return headerParam.kind === 'headerScalar' && headerParam.optional && headerParam.header.toLowerCase() === 'content-type';
1092
1092
  }
1093
+ function getOptionalContentTypeParam(paramGroups) {
1094
+ for (const headerParam of paramGroups.header) {
1095
+ if (isOptionalContentTypeHeader(headerParam)) {
1096
+ return headerParam;
1097
+ }
1098
+ }
1099
+ return undefined;
1100
+ }
1101
+ function constructPartialBodyRequestContent(indent, use, paramGroups, inClosure, bodyVarName) {
1102
+ if (paramGroups.partialBody.length === 0) {
1103
+ throw new CodegenError('InternalError', 'expected partial body params for request content construction');
1104
+ }
1105
+ const requestContentType = paramGroups.partialBody[0].type;
1106
+ use.addForType(requestContentType);
1107
+ let body = `${indent.get()}let ${bodyVarName}: ${inClosure ? 'Result<' : ''}${helpers.getTypeDeclaration(requestContentType)}${inClosure ? '>' : ''} = ${requestContentType.content.name} {\n`;
1108
+ indent.push();
1109
+ for (const partialBodyParam of paramGroups.partialBody) {
1110
+ if (partialBodyParam.type.content !== requestContentType.content) {
1111
+ throw new CodegenError('InternalError', `spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.name}, expected model type ${requestContentType.content.name}`);
1112
+ }
1113
+ if (partialBodyParam.optional) {
1114
+ body += `${indent.get()}${partialBodyParam.name}: options.${partialBodyParam.name}${inClosure ? '.clone()' : ''},\n`;
1115
+ continue;
1116
+ }
1117
+ let initializer = partialBodyParam.name;
1118
+ if (inClosure) {
1119
+ initializer += '.clone()';
1120
+ }
1121
+ if (requestContentType.content.visibility === 'pub') {
1122
+ initializer = `Some(${initializer})`;
1123
+ }
1124
+ if (initializer !== partialBodyParam.name) {
1125
+ initializer = `${partialBodyParam.name}: ${initializer}`;
1126
+ }
1127
+ body += `${indent.get()}${initializer},\n`;
1128
+ }
1129
+ body += `${indent.pop().get()}}.try_into()`;
1130
+ if (inClosure) {
1131
+ body += `;\n`;
1132
+ }
1133
+ else {
1134
+ body += `?;\n`;
1135
+ }
1136
+ return body;
1137
+ }
1093
1138
  /**
1094
1139
  * emits the code for building the HTTP request.
1095
1140
  * assumes that there's a local var 'url' which is the Url.
@@ -1111,13 +1156,7 @@ function constructRequest(indent, use, method, paramGroups, inClosure, urlVarNam
1111
1156
  const requestVarName = helpers.getUniqueVarName(method.params, ['request', 'core_req']);
1112
1157
  let body = `${indent.get()}let ${(forceMut || paramGroups.header.length > 0) ? 'mut ' : ''}${requestVarName} = Request::new(${urlVarName}${cloneUrl ? '.clone()' : ''}, Method::${utils.capitalize(method.httpMethod)});\n`;
1113
1158
  body += applyHeaderParams(indent, use, method, paramGroups, inClosure, requestVarName);
1114
- let optionalContentTypeParam;
1115
- for (const headerParam of paramGroups.header) {
1116
- // if the content-type header is optional, we need to emit it inside the "if let Some(body)" clause below.
1117
- if (isOptionalContentTypeHeader(headerParam)) {
1118
- optionalContentTypeParam = headerParam;
1119
- }
1120
- }
1159
+ const optionalContentTypeParam = getOptionalContentTypeParam(paramGroups);
1121
1160
  const bodyParam = paramGroups.body;
1122
1161
  if (bodyParam) {
1123
1162
  body += getParamValueHelper(indent, bodyParam, () => {
@@ -1130,52 +1169,37 @@ function constructRequest(indent, use, method, paramGroups, inClosure, urlVarNam
1130
1169
  });
1131
1170
  }
1132
1171
  else if (paramGroups.partialBody.length > 0) {
1133
- // all partial body params should point to the same underlying model type.
1134
- const requestContentType = paramGroups.partialBody[0].type;
1135
- use.addForType(requestContentType);
1136
1172
  if (inClosure) {
1137
- body += `${indent.get()}let body: Result<${helpers.getTypeDeclaration(requestContentType)}> = ${requestContentType.content.name} {\n`;
1138
- }
1139
- else {
1140
- body += `${indent.get()}let body: ${helpers.getTypeDeclaration(requestContentType)} = ${requestContentType.content.name} {\n`;
1141
- }
1142
- indent.push();
1143
- for (const partialBodyParam of paramGroups.partialBody) {
1144
- if (partialBodyParam.type.content !== requestContentType.content) {
1145
- throw new CodegenError('InternalError', `spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.content.name}, expected model type ${requestContentType.content.name}`);
1146
- }
1147
- if (partialBodyParam.optional) {
1148
- body += `${indent.get()}${partialBodyParam.name}: options.${partialBodyParam.name}${inClosure ? '.clone()' : ''},\n`;
1149
- continue;
1150
- }
1151
- let initializer = partialBodyParam.name;
1152
- if (inClosure) {
1153
- initializer = initializer + '.clone()';
1154
- }
1155
- if (requestContentType.content.visibility === 'pub') {
1156
- // spread param maps to a non-internal model, so it must be wrapped in Some()
1157
- initializer = `Some(${initializer})`;
1158
- }
1159
- // can't use shorthand init if it's more than just the param name
1160
- if (initializer !== partialBodyParam.name) {
1161
- initializer = `${partialBodyParam.name}: ${initializer}`;
1162
- }
1163
- body += `${indent.get()}${initializer},\n`;
1164
- }
1165
- if (inClosure) {
1166
- body += `${indent.pop().get()}}.try_into();\n`;
1167
- body += `${indent.get()}if let Ok(body) = body { ${requestVarName}.set_body(body); }\n`;
1168
- }
1169
- else {
1170
- body += `${indent.pop().get()}}.try_into()?;\n`;
1171
- body += `${indent.get()}${requestVarName}.set_body(body);\n`;
1173
+ throw new CodegenError('InternalError', 'partial body request construction in closures must use a dedicated fallible path');
1172
1174
  }
1175
+ body += constructPartialBodyRequestContent(indent, use, paramGroups, inClosure, 'body');
1176
+ body += `${indent.get()}${requestVarName}.set_body(body);\n`;
1173
1177
  }
1174
1178
  return {
1175
1179
  requestVarName: requestVarName,
1176
1180
  content: body
1177
1181
  };
1178
1182
  }
1183
+ function constructFallibleInitialPollerRequest(indent, use, method, paramGroups, requestVarName) {
1184
+ if (paramGroups.partialBody.length === 0) {
1185
+ throw new CodegenError('InternalError', 'expected partial body params for fallible poller request construction');
1186
+ }
1187
+ const optionalContentTypeParam = getOptionalContentTypeParam(paramGroups);
1188
+ let body = constructPartialBodyRequestContent(indent, use, paramGroups, true, 'result');
1189
+ body += `${indent.get()}match result {\n`;
1190
+ body += `${indent.push().get()}Ok(body) => {\n`;
1191
+ body += `${indent.push().get()}let mut ${requestVarName} = Request::new(url.clone(), Method::${utils.capitalize(method.httpMethod)});\n`;
1192
+ body += applyHeaderParams(indent, use, method, paramGroups, true, requestVarName);
1193
+ if (optionalContentTypeParam) {
1194
+ body += `${indent.get()}${requestVarName}.insert_header("${optionalContentTypeParam.header.toLowerCase()}", ${getHeaderPathQueryParamValue(use, optionalContentTypeParam, false, false)});\n`;
1195
+ }
1196
+ body += `${indent.get()}${requestVarName}.set_body(body);\n`;
1197
+ body += `${indent.get()}Ok((${requestVarName}, PollerContinuation::Links { next_link: url.clone(), final_link: None, }))\n`;
1198
+ body += `${indent.pop().get()}}\n`;
1199
+ body += `${indent.get()}Err(e) => Err(e),\n`;
1200
+ body += `${indent.pop().get()}}\n`;
1201
+ return body;
1202
+ }
1179
1203
  /**
1180
1204
  * Returns 'mut ' if the Url local var needs to be mutable, else the empty string.
1181
1205
  * @param paramGroups the param groups associated with the Url being constructed.
@@ -1274,6 +1298,110 @@ function getAsyncMethodBody(indent, use, client, method) {
1274
1298
  body += `${indent.get()}Ok(rsp.into())\n`;
1275
1299
  return body;
1276
1300
  }
1301
+ /**
1302
+ * returns the generated helper type name for a pageable or LRO method.
1303
+ *
1304
+ * @param client the client that owns the method
1305
+ * @param method the method that owns the helper type
1306
+ * @param suffix the helper type suffix
1307
+ * @returns the generated helper type name
1308
+ */
1309
+ function getMethodHelperTypeName(client, method, suffix) {
1310
+ return `${client.name}${utils.pascalCase(method.name, false)}${suffix}`;
1311
+ }
1312
+ /**
1313
+ * emits a local helper struct used for minimal response deserialization.
1314
+ *
1315
+ * @param indent the indentation helper currently in scope
1316
+ * @param helperStruct the helper struct to emit
1317
+ * @returns the helper struct source
1318
+ */
1319
+ function emitHelperStruct(indent, helperStruct) {
1320
+ let body = `${indent.get()}#[derive(serde::Deserialize)]\n`;
1321
+ body += `${indent.get()}struct ${helperStruct.name} {\n`;
1322
+ indent.push();
1323
+ for (const field of helperStruct.fields) {
1324
+ if (field.serdeName && field.serdeName !== field.name) {
1325
+ body += `${indent.get()}#[serde(rename = "${field.serdeName}")]\n`;
1326
+ }
1327
+ body += `${indent.get()}${field.name}: ${field.type},\n`;
1328
+ }
1329
+ body += `${indent.pop().get()}}\n`;
1330
+ return body;
1331
+ }
1332
+ /**
1333
+ * returns the helper struct(s) needed to deserialize only pageable continuation fields.
1334
+ *
1335
+ * @param indent the indentation helper currently in scope
1336
+ * @param client the client that owns the method
1337
+ * @param method the pageable method to inspect
1338
+ * @returns the helper struct source and root type name, or undefined if not needed
1339
+ */
1340
+ function getPageableResponseHelperStruct(indent, client, method) {
1341
+ var _a, _b;
1342
+ let nextLinkPath;
1343
+ if (((_a = method.strategy) === null || _a === void 0 ? void 0 : _a.kind) === 'nextLink') {
1344
+ nextLinkPath = method.strategy.nextLinkPath;
1345
+ }
1346
+ else if (((_b = method.strategy) === null || _b === void 0 ? void 0 : _b.kind) === 'continuationToken' && method.strategy.responseToken.kind === 'nextLink') {
1347
+ nextLinkPath = method.strategy.responseToken.nextLinkPath;
1348
+ }
1349
+ if (!nextLinkPath) {
1350
+ return undefined;
1351
+ }
1352
+ const typeName = getMethodHelperTypeName(client, method, 'Page');
1353
+ const helperStructNames = nextLinkPath.map((_, i) => i === 0 ? typeName : `${typeName}${i + 1}`);
1354
+ const helperStructs = new Array();
1355
+ for (let i = nextLinkPath.length - 1; i >= 0; --i) {
1356
+ const field = nextLinkPath[i];
1357
+ helperStructs.unshift({
1358
+ name: helperStructNames[i],
1359
+ fields: [{
1360
+ name: field.name,
1361
+ serdeName: field.serde,
1362
+ type: i === nextLinkPath.length - 1 ? 'Option<String>' : `Option<${helperStructNames[i + 1]}>`,
1363
+ }],
1364
+ });
1365
+ }
1366
+ let content = '';
1367
+ for (const helperStruct of helperStructs) {
1368
+ content += emitHelperStruct(indent, helperStruct);
1369
+ content += '\n';
1370
+ }
1371
+ return {
1372
+ content,
1373
+ typeName,
1374
+ };
1375
+ }
1376
+ // To add Monitor helpers for pollers, uncomment this helper and wire it into
1377
+ // getLroMethodBody.
1378
+ /*
1379
+ function getLroMonitorHelperStruct(indent: helpers.indentation, client: rust.Client, method: rust.LroMethod): { content: string; typeName: string; statusField: rust.ModelField } | undefined {
1380
+ const statusModel = helpers.unwrapType(method.returns.type);
1381
+ if (statusModel.kind !== 'model') {
1382
+ return undefined;
1383
+ }
1384
+
1385
+ const statusField = helpers.getStatusField(statusModel);
1386
+ if (!statusField) {
1387
+ return undefined;
1388
+ }
1389
+
1390
+ const typeName = getMethodHelperTypeName(client, method, 'Monitor');
1391
+ return {
1392
+ content: `${emitHelperStruct(indent, {
1393
+ name: typeName,
1394
+ fields: [{
1395
+ name: statusField.name,
1396
+ serdeName: statusField.serde,
1397
+ type: helpers.getTypeDeclaration(statusField.type),
1398
+ }],
1399
+ })}\n`,
1400
+ typeName,
1401
+ statusField,
1402
+ };
1403
+ }
1404
+ */
1277
1405
  /**
1278
1406
  * constructs the body for a pageable client method
1279
1407
  *
@@ -1284,7 +1412,7 @@ function getAsyncMethodBody(indent, use, client, method) {
1284
1412
  * @returns the contents of the method body
1285
1413
  */
1286
1414
  function getPageableMethodBody(indent, use, client, method) {
1287
- var _a, _b;
1415
+ var _a, _b, _c;
1288
1416
  use.add('azure_core::http', 'Method', 'Request', 'Url');
1289
1417
  use.add('azure_core::http::pager', 'PagerResult', 'PagerState');
1290
1418
  use.add('azure_core', 'Result');
@@ -1292,11 +1420,15 @@ function getPageableMethodBody(indent, use, client, method) {
1292
1420
  use.addForType(helpers.unwrapType(method.returns.type));
1293
1421
  const paramGroups = getMethodParamGroup(method);
1294
1422
  const urlVar = method.strategy ? 'first_url' : helpers.getUniqueVarName(method.params, ['url', 'url_var']);
1423
+ const pageableResponseHelperStruct = getPageableResponseHelperStruct(indent, client, method);
1295
1424
  let body = checkEmptyRequiredPathParams(indent, paramGroups.path);
1296
1425
  body += 'let options = options.unwrap_or_default().into_owned();\n';
1297
1426
  body += `${indent.get()}let pipeline = self.pipeline.clone();\n`;
1298
1427
  body += `${indent.get()}let ${urlVarNeedsMut(paramGroups, method)}${urlVar} = self.${client.endpoint.name}.clone();\n`;
1299
1428
  body += constructUrl(indent, use, method, paramGroups, urlVar);
1429
+ if (pageableResponseHelperStruct) {
1430
+ body += pageableResponseHelperStruct.content;
1431
+ }
1300
1432
  // passed to constructRequest. we only need to
1301
1433
  // clone it for the non-continuation case.
1302
1434
  let cloneUrl = false;
@@ -1415,7 +1547,7 @@ function getPageableMethodBody(indent, use, client, method) {
1415
1547
  use.add('azure_core', bodyFormat, 'http::RawResponse');
1416
1548
  body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
1417
1549
  const deserialize = `${bodyFormat}::from_${bodyFormat}`;
1418
- body += `${indent.get()}let res: ${helpers.getTypeDeclaration(helpers.unwrapType(method.returns.type))} = ${deserialize}(&body)?;\n`;
1550
+ body += `${indent.get()}let res: ${(_b = pageableResponseHelperStruct === null || pageableResponseHelperStruct === void 0 ? void 0 : pageableResponseHelperStruct.typeName) !== null && _b !== void 0 ? _b : helpers.getTypeDeclaration(helpers.unwrapType(method.returns.type))} = ${deserialize}(&body)?;\n`;
1419
1551
  body += `${indent.get()}let rsp = RawResponse::from_bytes(status, headers, body).into();\n`;
1420
1552
  }
1421
1553
  if (method.strategy) {
@@ -1483,7 +1615,7 @@ function getPageableMethodBody(indent, use, client, method) {
1483
1615
  // non-continuation case, so we don't need to worry about next links, continuation tokens, etc...
1484
1616
  body += `${indent.get()}Ok(PagerResult::Done { response: rsp.into() })\n`;
1485
1617
  }
1486
- if (((_b = method.strategy) === null || _b === void 0 ? void 0 : _b.kind) === 'nextLink') {
1618
+ if (((_c = method.strategy) === null || _c === void 0 ? void 0 : _c.kind) === 'nextLink') {
1487
1619
  body += `${indent.pop().get()}}\n`;
1488
1620
  }
1489
1621
  body += `${indent.pop().get()}})\n`; // end Box::pin(async move {
@@ -1529,7 +1661,9 @@ function getLroMethodBody(crate, indent, use, client, method) {
1529
1661
  body += `${indent.get()}let ${paramGroups.apiVersion.name} = ${getHeaderPathQueryParamValue(use, paramGroups.apiVersion, true, true)}.clone();\n`;
1530
1662
  }
1531
1663
  // we call this eagerly so that we have access to the request var name
1532
- const initialRequestResult = constructRequest(indent, use, method, paramGroups, true, urlVar, true, false);
1664
+ const initialRequestResult = paramGroups.partialBody.length > 0
1665
+ ? { requestVarName: helpers.getUniqueVarName(method.params, ['request', 'core_req']), content: '' }
1666
+ : constructRequest(indent, use, method, paramGroups, true, urlVar, true, false);
1533
1667
  const declareRequest = function (indent, use, method, paramGroups, requestVarName, linkExpr, forceMut, optionsPrefix) {
1534
1668
  let mutRequest = '';
1535
1669
  // if the only header is optional Content-Type it will not be used
@@ -1542,7 +1676,10 @@ function getLroMethodBody(crate, indent, use, client, method) {
1542
1676
  };
1543
1677
  body += `${indent.get()}Ok(${method.returns.type.name}::new(\n`;
1544
1678
  body += `${indent.push().get()}move |poller_state: PollerState, poller_options| {\n`;
1545
- body += `${indent.push().get()}let (mut ${initialRequestResult.requestVarName}, continuation) = ${helpers.buildMatch(indent, 'poller_state', [{
1679
+ const fallibleInitialPollerRequest = paramGroups.partialBody.length > 0
1680
+ ? constructFallibleInitialPollerRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName)
1681
+ : undefined;
1682
+ body += `${indent.push().get()}let ${fallibleInitialPollerRequest ? 'poller_request: Result<(Request, PollerContinuation)>' : `(mut ${initialRequestResult.requestVarName}, continuation)`} = ${helpers.buildMatch(indent, 'poller_state', [{
1546
1683
  pattern: `PollerState::More(continuation)`,
1547
1684
  body: (indent) => {
1548
1685
  var _a, _b;
@@ -1562,14 +1699,18 @@ function getLroMethodBody(crate, indent, use, client, method) {
1562
1699
  body += `${indent.get()}query_builder.build();\n`;
1563
1700
  }
1564
1701
  body += declareRequest(indent, use, method, paramGroups, initialRequestResult.requestVarName, 'next_link.clone()');
1565
- body += `${indent.get()}(${initialRequestResult.requestVarName}, PollerContinuation::Links { next_link, final_link })\n`;
1702
+ body += fallibleInitialPollerRequest
1703
+ ? `${indent.get()}Ok((${initialRequestResult.requestVarName}, PollerContinuation::Links { next_link, final_link }))\n`
1704
+ : `${indent.get()}(${initialRequestResult.requestVarName}, PollerContinuation::Links { next_link, final_link })\n`;
1566
1705
  return body;
1567
1706
  },
1568
1707
  }, {
1569
1708
  pattern: 'PollerState::Initial',
1570
1709
  body: (indent) => {
1571
- let body = initialRequestResult.content;
1572
- body += `${indent.get()}(${initialRequestResult.requestVarName}, PollerContinuation::Links { next_link: url.clone(), final_link: None, })\n`;
1710
+ let body = fallibleInitialPollerRequest !== null && fallibleInitialPollerRequest !== void 0 ? fallibleInitialPollerRequest : initialRequestResult.content;
1711
+ if (!fallibleInitialPollerRequest) {
1712
+ body += `${indent.get()}(${initialRequestResult.requestVarName}, PollerContinuation::Links { next_link: url.clone(), final_link: None, })\n`;
1713
+ }
1573
1714
  return body;
1574
1715
  },
1575
1716
  }])};\n`;
@@ -1630,6 +1771,10 @@ function getLroMethodBody(crate, indent, use, client, method) {
1630
1771
  body += 'let original_url = url.clone();\n';
1631
1772
  }
1632
1773
  body += `${indent.get()}Box::pin(async move {\n`;
1774
+ if (fallibleInitialPollerRequest) {
1775
+ body += `${indent.push().get()}let (mut ${initialRequestResult.requestVarName}, continuation) = poller_request?;\n`;
1776
+ indent.pop();
1777
+ }
1633
1778
  body += `${indent.push().get()}let rsp = pipeline.send(&ctx, &mut ${initialRequestResult.requestVarName}, ${getPipelineOptions(indent, use, method)}).await?;\n`;
1634
1779
  const needsMutBody = isArmPutLro || isArmPatchLro || isArmPostLro || isArmDeleteLro;
1635
1780
  body += `${indent.get()}let (status, headers, ${needsMutBody ? 'mut' : ''} body) = rsp.deconstruct();\n`;