@azure-tools/typespec-rust 0.1.0 → 0.2.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 (75) hide show
  1. package/dist/src/codegen/cargotoml.d.ts +6 -0
  2. package/dist/src/codegen/cargotoml.d.ts.map +1 -1
  3. package/dist/src/codegen/cargotoml.js +6 -1
  4. package/dist/src/codegen/cargotoml.js.map +1 -1
  5. package/dist/src/codegen/clients.d.ts +19 -2
  6. package/dist/src/codegen/clients.d.ts.map +1 -1
  7. package/dist/src/codegen/clients.js +483 -250
  8. package/dist/src/codegen/clients.js.map +1 -1
  9. package/dist/src/codegen/codeGenerator.d.ts +29 -6
  10. package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
  11. package/dist/src/codegen/codeGenerator.js +64 -25
  12. package/dist/src/codegen/codeGenerator.js.map +1 -1
  13. package/dist/src/codegen/context.d.ts +41 -0
  14. package/dist/src/codegen/context.d.ts.map +1 -1
  15. package/dist/src/codegen/context.js +94 -15
  16. package/dist/src/codegen/context.js.map +1 -1
  17. package/dist/src/codegen/enums.d.ts +9 -1
  18. package/dist/src/codegen/enums.d.ts.map +1 -1
  19. package/dist/src/codegen/enums.js +34 -20
  20. package/dist/src/codegen/enums.js.map +1 -1
  21. package/dist/src/codegen/helpers.d.ts +116 -2
  22. package/dist/src/codegen/helpers.d.ts.map +1 -1
  23. package/dist/src/codegen/helpers.js +148 -18
  24. package/dist/src/codegen/helpers.js.map +1 -1
  25. package/dist/src/codegen/lib.d.ts +7 -1
  26. package/dist/src/codegen/lib.d.ts.map +1 -1
  27. package/dist/src/codegen/lib.js +10 -5
  28. package/dist/src/codegen/lib.js.map +1 -1
  29. package/dist/src/codegen/mod.d.ts +6 -1
  30. package/dist/src/codegen/mod.d.ts.map +1 -1
  31. package/dist/src/codegen/mod.js +9 -10
  32. package/dist/src/codegen/mod.js.map +1 -1
  33. package/dist/src/codegen/models.d.ts +20 -1
  34. package/dist/src/codegen/models.d.ts.map +1 -1
  35. package/dist/src/codegen/models.js +250 -13
  36. package/dist/src/codegen/models.js.map +1 -1
  37. package/dist/src/codegen/use.d.ts +32 -0
  38. package/dist/src/codegen/use.d.ts.map +1 -1
  39. package/dist/src/codegen/use.js +42 -15
  40. package/dist/src/codegen/use.js.map +1 -1
  41. package/dist/src/codemodel/client.d.ts +150 -11
  42. package/dist/src/codemodel/client.d.ts.map +1 -1
  43. package/dist/src/codemodel/client.js +60 -17
  44. package/dist/src/codemodel/client.js.map +1 -1
  45. package/dist/src/codemodel/crate.d.ts +33 -0
  46. package/dist/src/codemodel/crate.d.ts.map +1 -1
  47. package/dist/src/codemodel/crate.js +17 -0
  48. package/dist/src/codemodel/crate.js.map +1 -1
  49. package/dist/src/codemodel/method.d.ts +17 -0
  50. package/dist/src/codemodel/method.d.ts.map +1 -1
  51. package/dist/src/codemodel/types.d.ts +152 -24
  52. package/dist/src/codemodel/types.d.ts.map +1 -1
  53. package/dist/src/codemodel/types.js +39 -25
  54. package/dist/src/codemodel/types.js.map +1 -1
  55. package/dist/src/emitter.d.ts +5 -0
  56. package/dist/src/emitter.d.ts.map +1 -1
  57. package/dist/src/emitter.js +20 -20
  58. package/dist/src/emitter.js.map +1 -1
  59. package/dist/src/lib.d.ts +1 -0
  60. package/dist/src/lib.d.ts.map +1 -1
  61. package/dist/src/lib.js +1 -0
  62. package/dist/src/lib.js.map +1 -1
  63. package/dist/src/tcgcadapter/adapter.d.ts +73 -1
  64. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  65. package/dist/src/tcgcadapter/adapter.js +338 -71
  66. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  67. package/dist/src/tcgcadapter/helpers.d.ts +18 -0
  68. package/dist/src/tcgcadapter/helpers.d.ts.map +1 -1
  69. package/dist/src/tcgcadapter/helpers.js +23 -2
  70. package/dist/src/tcgcadapter/helpers.js.map +1 -1
  71. package/dist/src/tcgcadapter/naming.d.ts +8 -0
  72. package/dist/src/tcgcadapter/naming.d.ts.map +1 -1
  73. package/dist/src/tcgcadapter/naming.js +8 -5
  74. package/dist/src/tcgcadapter/naming.js.map +1 -1
  75. package/package.json +21 -23
@@ -4,30 +4,38 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as codegen from '@azure-tools/codegen';
6
6
  import * as helpers from './helpers.js';
7
+ import queryString from 'query-string';
7
8
  import { Use } from './use.js';
8
- // emits the content for all client files
9
- export function emitClients(crate) {
10
- const clientFiles = new Array();
9
+ import * as rust from '../codemodel/index.js';
10
+ /**
11
+ * emits the content for all client files
12
+ *
13
+ * @param crate the crate for which to emit clients
14
+ * @param targetDir the directory to contain the client content
15
+ * @returns client content or undefined if the crate contains no clients
16
+ */
17
+ export function emitClients(crate, targetDir) {
11
18
  if (crate.clients.length === 0) {
12
- return clientFiles;
19
+ return undefined;
13
20
  }
21
+ const clientFiles = new Array();
14
22
  const clientMods = new Array();
15
23
  // emit the clients, one file per client
16
24
  for (const client of crate.clients) {
17
25
  const use = new Use();
18
- const indentation = new helpers.indentation();
26
+ const indent = new helpers.indentation();
19
27
  let pubInClients = '';
20
28
  if (!client.constructable) {
21
29
  // the constructable client will need access to these fields
22
- pubInClients = 'pub(in crate::generated::clients) ';
30
+ pubInClients = 'pub(crate) ';
23
31
  }
24
32
  let body = `pub struct ${client.name} {\n`;
25
33
  for (const field of client.fields) {
26
34
  use.addForType(field.type);
27
- body += `${indentation.get()}${pubInClients}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
35
+ body += `${indent.get()}${pubInClients}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
28
36
  }
29
37
  use.addType('azure_core', 'Pipeline');
30
- body += `${indentation.get()}${pubInClients}pipeline: Pipeline,\n`;
38
+ body += `${indent.get()}${pubInClients}pipeline: Pipeline,\n`;
31
39
  body += '}\n\n'; // end client
32
40
  if (client.constructable) {
33
41
  body += '#[derive(Clone, Debug)]\n';
@@ -36,7 +44,7 @@ export function emitClients(crate) {
36
44
  body += ' {\n';
37
45
  for (const field of client.constructable.options.type.fields) {
38
46
  use.addForType(field.type);
39
- body += `${indentation.get()}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
47
+ body += `${indent.get()}${helpers.emitPub(field.pub)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
40
48
  }
41
49
  body += '}\n\n'; // end client options
42
50
  }
@@ -50,33 +58,33 @@ export function emitClients(crate) {
50
58
  use.addType('azure_core', 'Result');
51
59
  for (let i = 0; i < client.constructable.constructors.length; ++i) {
52
60
  const constructor = client.constructable.constructors[i];
53
- body += `${indentation.get()}pub fn ${constructor.name}(${getConstructorParamsSig(constructor.parameters, client.constructable.options, use)}) -> Result<Self> {\n`;
61
+ body += `${indent.get()}pub fn ${constructor.name}(${getConstructorParamsSig(constructor.parameters, client.constructable.options, use)}) -> Result<Self> {\n`;
54
62
  // by convention, the endpoint param is always the first ctor param
55
63
  const endpointParamName = constructor.parameters[0].name;
56
- body += `${indentation.push().get()}let mut ${endpointParamName} = Url::parse(${endpointParamName}.as_ref())?;\n`;
57
- body += `${indentation.get()}${endpointParamName}.query_pairs_mut().clear();\n`;
64
+ body += `${indent.push().get()}let mut ${endpointParamName} = Url::parse(${endpointParamName}.as_ref())?;\n`;
65
+ body += `${indent.get()}${endpointParamName}.set_query(None);\n`;
58
66
  // if there's a credential param, create the necessary auth policy
59
67
  const authPolicy = getAuthPolicy(constructor, use);
60
68
  if (authPolicy) {
61
- body += `${indentation.get()}${authPolicy}\n`;
69
+ body += `${indent.get()}${authPolicy}\n`;
62
70
  }
63
- body += `${indentation.get()}let options = options.unwrap_or_default();\n`;
64
- body += `${indentation.get()}Ok(Self {\n`;
71
+ body += `${indent.get()}let options = options.unwrap_or_default();\n`;
72
+ body += `${indent.get()}Ok(Self {\n`;
65
73
  // propagate any client option fields to the client initializer
66
- indentation.push();
74
+ indent.push();
67
75
  for (const field of getClientOptionsFields(client.constructable.options)) {
68
- body += `${indentation.get()}${field.name}: options.${field.name},\n`;
76
+ body += `${indent.get()}${field.name}: options.${field.name},\n`;
69
77
  }
70
- body += `${indentation.get()}${endpointParamName},\n`;
71
- body += `${indentation.get()}pipeline: Pipeline::new(\n`;
72
- body += `${indentation.push().get()}option_env!("CARGO_PKG_NAME"),\n`;
73
- body += `${indentation.get()}option_env!("CARGO_PKG_VERSION"),\n`;
74
- body += `${indentation.get()}options.client_options,\n`;
75
- body += `${indentation.get()}Vec::default(),\n`;
76
- body += `${indentation.get()}${authPolicy ? 'vec![auth_policy]' : 'Vec::default()'},\n`;
77
- body += `${indentation.pop().get()}),\n`; // end Pipeline::new
78
- body += `${indentation.pop().get()}})\n`; // end Ok
79
- body += `${indentation.pop().get()}}\n`; // end constructor
78
+ body += `${indent.get()}${endpointParamName},\n`;
79
+ body += `${indent.get()}pipeline: Pipeline::new(\n`;
80
+ body += `${indent.push().get()}option_env!("CARGO_PKG_NAME"),\n`;
81
+ body += `${indent.get()}option_env!("CARGO_PKG_VERSION"),\n`;
82
+ body += `${indent.get()}options.client_options,\n`;
83
+ body += `${indent.get()}Vec::default(),\n`;
84
+ body += `${indent.get()}${authPolicy ? 'vec![auth_policy]' : 'Vec::default()'},\n`;
85
+ body += `${indent.pop().get()}),\n`; // end Pipeline::new
86
+ body += `${indent.pop().get()}})\n`; // end Ok
87
+ body += `${indent.pop().get()}}\n`; // end constructor
80
88
  // ensure extra new-line between ctors and/or client methods
81
89
  if (i + 1 < client.constructable.constructors.length || client.methods.length > 0) {
82
90
  body += '\n';
@@ -85,8 +93,8 @@ export function emitClients(crate) {
85
93
  }
86
94
  for (let i = 0; i < client.methods.length; ++i) {
87
95
  const method = client.methods[i];
88
- let returnType;
89
- let async = 'async ';
96
+ const returnType = helpers.getTypeDeclaration(method.returns);
97
+ let async = '';
90
98
  // NOTE: when methodBody is called, the starting indentation
91
99
  // will be correct for the current scope, so there's no need
92
100
  // for the callee to indent right away.
@@ -94,52 +102,47 @@ export function emitClients(crate) {
94
102
  use.addForType(method.returns);
95
103
  switch (method.kind) {
96
104
  case 'async':
97
- returnType = helpers.getTypeDeclaration(method.returns);
105
+ async = 'async ';
98
106
  methodBody = (indentation) => {
99
107
  return getAsyncMethodBody(indentation, use, client, method);
100
108
  };
101
109
  break;
110
+ case 'pageable':
111
+ methodBody = (indentation) => {
112
+ return getPageableMethodBody(indentation, use, client, method);
113
+ };
114
+ break;
102
115
  case 'clientaccessor':
103
- async = '';
104
- returnType = method.returns.name;
105
116
  methodBody = (indentation) => {
106
117
  return getClientAccessorMethodBody(indentation, method);
107
118
  };
108
119
  break;
109
120
  }
110
- body += `${indentation.get()}${helpers.formatDocComment(method.docs)}`;
111
- body += `${indentation.get()}${helpers.emitPub(method.pub)}${async}fn ${method.name}(${getMethodParamsSig(method, use)}) -> ${returnType} {\n`;
112
- body += `${indentation.push().get()}${methodBody(indentation)}\n`;
113
- body += `${indentation.pop().get()}}\n`; // end method
121
+ body += `${indent.get()}${helpers.formatDocComment(method.docs)}`;
122
+ body += `${indent.get()}${helpers.emitPub(method.pub)}${async}fn ${method.name}(${getMethodParamsSig(method, use)}) -> ${returnType} {\n`;
123
+ body += `${indent.push().get()}${methodBody(indent)}\n`;
124
+ body += `${indent.pop().get()}}\n`; // end method
114
125
  if (i + 1 < client.methods.length) {
115
126
  body += '\n';
116
127
  }
117
128
  }
118
129
  body += '}\n\n'; // end client impl
119
130
  if (client.constructable) {
120
- // emit the builder function for client options
121
- const clientOptionsType = client.constructable.options;
122
- const builderTypeName = getOptionsBuilderTypeName(clientOptionsType, false);
123
- body += `impl ${clientOptionsType.type.name} {\n`;
124
- body += `${indentation.get()}pub fn builder() -> builders::${builderTypeName} {\n`;
125
- body += `${indentation.push().get()}builders::${builderTypeName}::new()\n`;
126
- body += `${indentation.pop().get()}}\n`;
127
- body += '}\n\n'; // end impl
128
131
  // emit default trait impl for client options type
132
+ const clientOptionsType = client.constructable.options;
129
133
  body += `impl Default for ${clientOptionsType.type.name} {\n`;
130
- body += `${indentation.get()}fn default() -> Self {\n`;
131
- body += `${indentation.push().get()}Self {\n`;
132
- indentation.push();
134
+ body += `${indent.get()}fn default() -> Self {\n`;
135
+ body += `${indent.push().get()}Self {\n`;
136
+ indent.push();
133
137
  for (const field of clientOptionsType.type.fields) {
134
138
  if (!field.defaultValue) {
135
139
  throw new Error(`missing default value for struct field ${clientOptionsType.type.name}.${field.name}`);
136
140
  }
137
- body += `${indentation.get()}${field.name}: ${field.defaultValue},\n`;
141
+ body += `${indent.get()}${field.name}: ${field.defaultValue},\n`;
138
142
  }
139
- body += `${indentation.pop().get()}}\n`;
140
- body += `${indentation.pop().get()}}\n`;
143
+ body += `${indent.pop().get()}}\n`;
144
+ body += `${indent.pop().get()}}\n`;
141
145
  body += '}\n\n'; // end impl
142
- body += createClientOptionsBuilderImpl(indentation, clientOptionsType, use);
143
146
  }
144
147
  // emit method options
145
148
  for (let i = 0; i < client.methods.length; ++i) {
@@ -151,70 +154,99 @@ export function emitClients(crate) {
151
154
  body += `${helpers.emitPub(method.pub)}struct ${helpers.getTypeDeclaration(method.options.type)} {\n`;
152
155
  for (const field of method.options.type.fields) {
153
156
  use.addForType(field.type);
154
- body += `${indentation.get()}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
157
+ body += `${indent.get()}${helpers.emitPub(field.pub)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
155
158
  }
156
159
  body += '}\n\n'; // end options
157
- body += `impl${getLifetimeAnnotation(method.options.type)}${helpers.getTypeDeclaration(method.options.type)} {\n`;
158
- body += `${indentation.get()}pub fn builder() -> builders::${getOptionsBuilderTypeName(method.options, true)} {\n`;
159
- body += `${indentation.push().get()}builders::${getOptionsBuilderTypeName(method.options, false)}::new()\n`;
160
- body += `${indentation.pop().get()}}\n`; // end builder()
161
- body += '}\n'; // end options impl
160
+ if (method.kind === 'pageable') {
161
+ body += `impl${getLifetimeAnnotation(method.options.type)} ${helpers.getTypeDeclaration(method.options.type)} {\n`;
162
+ body += `${indent.get()}pub fn into_owned(self) -> ${method.options.type.name}<'static> {\n`;
163
+ body += `${indent.push().get()}${method.options.type.name} {\n`;
164
+ indent.push();
165
+ for (const field of method.options.type.fields) {
166
+ if (field.type.kind === 'external' && field.type.name === 'ClientMethodOptions') {
167
+ body += `${indent.get()}${field.name}: ClientMethodOptions {\n`;
168
+ body += `${indent.push().get()}context: self.${field.name}.context.into_owned(),\n`;
169
+ body += `${indent.pop().get()}},\n`;
170
+ continue;
171
+ }
172
+ body += `${indent.get()}${field.name}: self.${field.name},\n`;
173
+ }
174
+ body += `${indent.pop().get()}}\n`;
175
+ body += `${indent.pop().get()}}\n`;
176
+ body += '}\n';
177
+ }
162
178
  if (i + 1 < client.methods.length) {
163
179
  body += '\n';
164
180
  }
165
181
  }
166
182
  body += '\n';
167
- body += createPubModBuilders(client, use);
168
183
  let content = helpers.contentPreamble();
169
184
  content += use.text();
170
185
  content += body;
171
186
  const clientMod = codegen.deconstruct(client.name).join('_');
172
- clientFiles.push({ name: `${clientMod}.rs`, content: content });
173
- clientMods.push(clientMod);
174
- }
175
- // now emit the mod.rs file for the clients
176
- let content = helpers.contentPreamble();
177
- const sortedMods = clientMods.sort((a, b) => { return helpers.sortAscending(a, b); });
178
- for (const clientMod of sortedMods) {
179
- content += `pub mod ${clientMod};\n`;
187
+ clientFiles.push({ name: `${targetDir}/${clientMod}.rs`, content: content });
188
+ clientMods.push(new rust.Module(clientMod, true));
180
189
  }
181
- clientFiles.push({ name: 'mod.rs', content: content });
182
- return clientFiles;
190
+ return { clients: clientFiles, modules: clientMods };
183
191
  }
192
+ /**
193
+ * creates the parameter signature for a client constructor
194
+ * e.g. "foo: i32, bar: String, options: ClientOptions"
195
+ *
196
+ * @param params the params to include in the signature. can be empty
197
+ * @param options the client options type. will always be the last parameter
198
+ * @param use the use statement builder currently in scope
199
+ * @returns the client constructor params sig
200
+ */
184
201
  function getConstructorParamsSig(params, options, use) {
185
202
  const paramsSig = new Array();
186
203
  for (const param of params) {
187
204
  use.addForType(param.type);
188
- paramsSig.push(`${param.name}: ${helpers.getTypeDeclaration(param.type)}`);
205
+ paramsSig.push(`${param.name}: ${param.ref ? '&' : ''}${helpers.getTypeDeclaration(param.type)}`);
189
206
  }
190
207
  paramsSig.push(`options: ${helpers.getTypeDeclaration(options)}`);
191
208
  return paramsSig.join(', ');
192
209
  }
210
+ /**
211
+ * creates the parameter signature for a client method
212
+ * e.g. "foo: i32, bar: String, options: MethodOptions"
213
+ *
214
+ * @param method the Rust method for which to create the param sig
215
+ * @param use the use statement builder currently in scope
216
+ * @returns the method params sig
217
+ */
193
218
  function getMethodParamsSig(method, use) {
194
219
  const paramsSig = new Array();
195
220
  paramsSig.push(formatParamTypeName(method.self));
196
- for (const param of method.params) {
197
- if (param.type.kind === 'literal') {
198
- // literal params are embedded directly in the code (e.g. accept header param)
199
- continue;
200
- }
201
- // don't add client params to the method param sig
202
- if (method.kind !== 'clientaccessor' && param.location === 'method') {
203
- use.addForType(param.type);
204
- paramsSig.push(`${param.name}: ${formatParamTypeName(param)}`);
205
- }
206
- }
221
+ // client accessors only have a self param so skip checking for params
207
222
  if (method.kind !== 'clientaccessor') {
223
+ for (const param of method.params) {
224
+ if (param.type.kind === 'literal') {
225
+ // literal params are embedded directly in the code (e.g. accept header param)
226
+ continue;
227
+ }
228
+ // don't add client or optional params to the method param sig
229
+ if (param.location === 'method' && !param.optional) {
230
+ use.addForType(param.type);
231
+ paramsSig.push(`${param.name}: ${formatParamTypeName(param)}`);
232
+ }
233
+ }
208
234
  paramsSig.push(`options: ${helpers.getTypeDeclaration(method.options, true)}`);
209
235
  }
210
236
  return paramsSig.join(', ');
211
237
  }
212
- // creates the auth policy if the ctor contains a credential param.
213
- // the policy will be named auth_policy.
238
+ /**
239
+ * returns the auth policy instantiation code if the ctor contains a credential param.
240
+ * the policy will be a local var named auth_policy.
241
+ *
242
+ * @param ctor the constructor for which to instantiate an auth policy
243
+ * @param use the use statement builder currently in scope
244
+ * @returns the auth policy instantiation code or undefined if not required
245
+ */
214
246
  function getAuthPolicy(ctor, use) {
215
247
  for (const param of ctor.parameters) {
216
248
  if (param.type.kind === 'arc' && param.type.type.kind === 'tokenCredential') {
217
- use.addType('azure_core', 'BearerTokenCredentialPolicy');
249
+ use.addTypes('azure_core', ['BearerTokenCredentialPolicy', 'Policy']);
218
250
  const scopes = new Array();
219
251
  for (const scope of param.type.type.scopes) {
220
252
  scopes.push(`"${scope}"`);
@@ -224,6 +256,13 @@ function getAuthPolicy(ctor, use) {
224
256
  }
225
257
  return undefined;
226
258
  }
259
+ /**
260
+ * returns the complete text for the provided parameter's type
261
+ * e.g. self, &String, mut SomeStruct
262
+ *
263
+ * @param param the parameter for which to create the
264
+ * @returns the parameter's type declaration
265
+ */
227
266
  function formatParamTypeName(param) {
228
267
  let format = '';
229
268
  if (param.ref) {
@@ -232,118 +271,39 @@ function formatParamTypeName(param) {
232
271
  if (param.mut) {
233
272
  format += 'mut ';
234
273
  }
235
- if (param.type) {
236
- format += helpers.getTypeDeclaration(param.type);
274
+ if (param.kind) {
275
+ const methodParam = param;
276
+ let paramType = methodParam.type;
277
+ if (methodParam.kind === 'partialBody') {
278
+ // for partial body params, methodParam.type is the model type that's
279
+ // sent in the request. we want the field within the model for this param.
280
+ const field = methodParam.type.type.fields.find(f => { return f.name === methodParam.name; });
281
+ if (!field) {
282
+ throw new Error(`didn't find spread param field ${methodParam.name} in type ${methodParam.type.type.name}`);
283
+ }
284
+ paramType = field.type;
285
+ if (paramType.kind === 'option') {
286
+ // this is the case where the spread param maps to a non-internal model.
287
+ // for this case, we unwrap the Option<T> to get the underlying type.
288
+ paramType = paramType.type;
289
+ }
290
+ }
291
+ format += helpers.getTypeDeclaration(paramType);
237
292
  }
238
293
  else {
239
294
  format += param.name;
240
295
  }
241
296
  return format;
242
297
  }
243
- function createClientOptionsBuilderImpl(indentation, options, use) {
244
- use.addType('azure_core::builders', 'ClientOptionsBuilder');
245
- use.addTypes('azure_core', ['Policy', 'RetryOptions', /*'TelemetryOptions', */ 'TransportOptions']);
246
- use.addType('std::sync', 'Arc');
247
- const emitWithMethod = function (indentation, name, into) {
248
- let withPolicies = `${indentation.get()}fn with_${name}<P>(mut self, ${name}: P) -> Self `;
249
- withPolicies += `where P: Into<${into}>, Self: Sized {\n`;
250
- withPolicies += `${indentation.push().get()}self.client_options.set_${name}(${name});\n`;
251
- withPolicies += `${indentation.get()}self\n`;
252
- withPolicies += `${indentation.pop().get()}}\n\n`;
253
- return withPolicies;
254
- };
255
- let implBuilder = `impl ClientOptionsBuilder for ${options.type.name} {\n`;
256
- implBuilder += emitWithMethod(indentation, 'per_call_policies', 'Vec<Arc<dyn Policy>>');
257
- implBuilder += emitWithMethod(indentation, 'per_try_policies', 'Vec<Arc<dyn Policy>>');
258
- implBuilder += emitWithMethod(indentation, 'retry', 'RetryOptions');
259
- // TODO: https://github.com/Azure/azure-sdk-for-rust/issues/1753
260
- //implBuilder += emitWithMethod(indentation, 'telemetry', 'TelemetryOptions');
261
- implBuilder += emitWithMethod(indentation, 'transport', 'TransportOptions');
262
- implBuilder += '}\n\n';
263
- return implBuilder;
264
- }
265
- function createPubModBuilders(client, use) {
266
- const indentation = new helpers.indentation();
267
- let body = 'pub mod builders {\n';
268
- body += `${indentation.get()}use super::*;\n\n`;
269
- const emitNewFunction = function (indentation, options) {
270
- let newFunction = `${indentation.push().get()}pub(super) fn new() -> Self {\n`;
271
- newFunction += `${indentation.push().get()}Self {\n`;
272
- newFunction += `${indentation.push().get()}options: ${options.type.name}::default(),\n`;
273
- newFunction += `${indentation.pop().get()}}\n`;
274
- newFunction += `${indentation.pop().get()}}\n\n`;
275
- return newFunction;
276
- };
277
- const emitBuildMethod = function (indentation, options) {
278
- let buildMethod = `${indentation.get()}pub fn build(&self) -> ${options.type.name} {\n`;
279
- buildMethod += `${indentation.push().get()}self.options.clone()\n`;
280
- buildMethod += `${indentation.pop().get()}}\n`;
281
- return buildMethod;
282
- };
283
- if (client.constructable) {
284
- // emit the builder type for client options
285
- const clientOptionsType = client.constructable.options;
286
- const builderTypeName = getOptionsBuilderTypeName(clientOptionsType, false);
287
- body += `${indentation.get()}pub struct ${builderTypeName} {\n`;
288
- body += `${indentation.push().get()}options: ${clientOptionsType.type.name},\n`;
289
- body += `${indentation.pop().get()}}\n\n`; // end struct
290
- body += `${indentation.get()}impl ${builderTypeName} {\n`;
291
- body += emitNewFunction(indentation, clientOptionsType);
292
- body += emitBuildMethod(indentation, clientOptionsType);
293
- for (const field of getClientOptionsFields(client.constructable.options)) {
294
- let typeName = helpers.getTypeDeclaration(field.type);
295
- let into = '';
296
- if (field.type.kind === 'String') {
297
- // for Strings, we define these as "impl Into<String>" so that passing a str will just work
298
- typeName = `impl Into<${typeName}>`;
299
- into = '.into()';
300
- }
301
- body += `\n${indentation.get()}pub fn with_${field.name}(mut self, ${field.name}: ${typeName}) -> Self {\n`;
302
- body += `${indentation.push().get()}self.options.${field.name} = ${field.name}${into};\n`;
303
- body += `${indentation.get()}self\n`;
304
- body += `${indentation.pop().get()}}\n`;
305
- }
306
- body += `${indentation.pop().get()}}\n\n`; // end impl
307
- }
308
- // emit the client method options builders
309
- for (let i = 0; i < client.methods.length; ++i) {
310
- const method = client.methods[i];
311
- if (method.kind === 'clientaccessor') {
312
- continue;
313
- }
314
- use.addType('azure_core', 'Context');
315
- use.addType('azure_core::builders', 'ClientMethodOptionsBuilder');
316
- const optionsBuilderTypeName = getOptionsBuilderTypeName(method.options, true);
317
- body += `${indentation.get()}pub struct ${optionsBuilderTypeName} {\n`;
318
- body += `${indentation.push().get()}options: ${helpers.getTypeDeclaration(method.options.type)},\n`;
319
- body += `${indentation.pop().get()}}\n\n`; // end struct
320
- body += `${indentation.get()}impl ${getOptionsBuilderTypeName(method.options, 'anonymous')} {\n`;
321
- body += emitNewFunction(indentation, method.options);
322
- body += emitBuildMethod(indentation, method.options);
323
- body += `${indentation.pop().get()}}\n\n`; // end impl
324
- body += `${indentation.get()}impl${getLifetimeAnnotation(method.options.type)}ClientMethodOptionsBuilder${getLifetimeAnnotation(method.options.type)}for ${optionsBuilderTypeName} {\n`;
325
- body += `${indentation.push().get()}fn with_context(mut self, context: &${getLifetimeName(method.options.type)}Context) -> Self {\n`;
326
- body += `${indentation.push().get()}self.options.${getClientMethodOptionsFieldName(method.options)}.set_context(context);\n`;
327
- body += `${indentation.get()}self\n`;
328
- body += `${indentation.pop().get()}}\n`; // end with_context
329
- body += `${indentation.pop().get()}}\n`; // end ClientMethodOptionsBuilder impl
330
- if (i + 1 < client.methods.length) {
331
- body += '\n';
332
- }
333
- }
334
- body += '}\n'; // end pub mod builders
335
- return body;
336
- }
337
- function getOptionsBuilderTypeName(option, withLifetime) {
338
- if (!withLifetime || !option.type.lifetime) {
339
- return `${option.type.name}Builder`;
340
- }
341
- else if (withLifetime === 'anonymous') {
342
- return `${option.type.name}Builder${helpers.AnonymousLifetimeAnnotation}`;
343
- }
344
- return `${option.type.name}Builder${helpers.getGenericLifetimeAnnotation(option.type.lifetime)}`;
345
- }
346
- // returns a filtered array of struct fields from the client options types
298
+ /**
299
+ * returns a subset of struct fields from the client options types.
300
+ * at present, only the core ClientOptions field is omitted.
301
+ *
302
+ * can return an empty array if there are no client options.
303
+ *
304
+ * @param option the client options type to enumerate
305
+ * @returns the subset of struct fields
306
+ */
347
307
  function getClientOptionsFields(option) {
348
308
  const fields = new Array();
349
309
  for (const field of option.type.fields) {
@@ -355,27 +315,12 @@ function getClientOptionsFields(option) {
355
315
  }
356
316
  return fields;
357
317
  }
358
- function getClientMethodOptionsFieldName(option) {
359
- for (const field of option.type.fields) {
360
- // startsWith to ignore possible lifetime annotation suffix
361
- if (helpers.getTypeDeclaration(field.type).startsWith('ClientMethodOptions')) {
362
- return field.name;
363
- }
364
- }
365
- throw new Error(`didn't find ClientMethodOptions field in ${option.type.name}`);
366
- }
367
- function getLifetimeAnnotation(type) {
368
- if (type.lifetime) {
369
- return `${helpers.getGenericLifetimeAnnotation(type.lifetime)} `;
370
- }
371
- return ' ';
372
- }
373
- function getLifetimeName(type) {
374
- if (type.lifetime) {
375
- return `${type.lifetime.name} `;
376
- }
377
- return ' ';
378
- }
318
+ /**
319
+ * returns the name of the endpoint field within the client
320
+ *
321
+ * @param client the client in which to find the endpoint field
322
+ * @returns the name of the endpoint field
323
+ */
379
324
  function getEndpointFieldName(client) {
380
325
  // find the endpoint field. it's the only one that's
381
326
  // a Url. the name will be uniform across clients
@@ -393,29 +338,43 @@ function getEndpointFieldName(client) {
393
338
  }
394
339
  return endpointFieldName;
395
340
  }
396
- function getClientAccessorMethodBody(indentation, clientAccessor) {
341
+ /**
342
+ * constructs the body for a client accessor method
343
+ *
344
+ * @param indent the indentation helper currently in scope
345
+ * @param clientAccessor the client accessor for which to construct the body
346
+ * @returns the contents of the method body
347
+ */
348
+ function getClientAccessorMethodBody(indent, clientAccessor) {
397
349
  let body = `${clientAccessor.returns.name} {\n`;
398
350
  const endpointFieldName = getEndpointFieldName(clientAccessor.returns);
399
- body += `${indentation.push().get()}${endpointFieldName}: self.${endpointFieldName}.clone(),\n`;
400
- body += `${indentation.get()}pipeline: self.pipeline.clone(),\n`;
401
- body += `${indentation.pop().get()}}`;
351
+ body += `${indent.push().get()}${endpointFieldName}: self.${endpointFieldName}.clone(),\n`;
352
+ body += `${indent.get()}pipeline: self.pipeline.clone(),\n`;
353
+ body += `${indent.pop().get()}}`;
402
354
  return body;
403
355
  }
404
- function getAsyncMethodBody(indentation, use, client, method) {
405
- use.addTypes('azure_core', ['AsClientMethodOptions', 'Method', 'Request']);
406
- let body = 'let options = options.unwrap_or_default();\n';
407
- body += `${indentation.get()}let mut ctx = options.method_options.context();\n`;
408
- body += `${indentation.get()}let mut url = self.${getEndpointFieldName(client)}.clone();\n`;
356
+ /**
357
+ * enumerates method parameters and returns them based on groups
358
+ *
359
+ * @param method the method containing the parameters to group
360
+ * @returns the groups parameters
361
+ */
362
+ function getMethodParamGroup(method) {
409
363
  // collect and sort all the header/path/query params
410
364
  const headerParams = new Array();
411
365
  const pathParams = new Array();
412
366
  const queryParams = new Array();
367
+ const partialBodyParams = new Array();
413
368
  for (const param of method.params) {
414
369
  switch (param.kind) {
415
370
  case 'header':
416
371
  case 'headerCollection':
372
+ case 'headerHashMap':
417
373
  headerParams.push(param);
418
374
  break;
375
+ case 'partialBody':
376
+ partialBodyParams.push(param);
377
+ break;
419
378
  case 'path':
420
379
  pathParams.push(param);
421
380
  break;
@@ -428,65 +387,322 @@ function getAsyncMethodBody(indentation, use, client, method) {
428
387
  headerParams.sort((a, b) => { return helpers.sortAscending(a.header, b.header); });
429
388
  pathParams.sort((a, b) => { return helpers.sortAscending(a.segment, b.segment); });
430
389
  queryParams.sort((a, b) => { return helpers.sortAscending(a.key, b.key); });
431
- let path = `"${method.httpPath}"`;
432
- if (pathParams.length > 0) {
390
+ let bodyParam;
391
+ for (const param of method.params) {
392
+ if (param.kind === 'body') {
393
+ if (bodyParam) {
394
+ throw new Error(`method ${method.name} has multiple body parameters`);
395
+ }
396
+ bodyParam = param;
397
+ }
398
+ }
399
+ return {
400
+ body: bodyParam,
401
+ header: headerParams,
402
+ partialBody: partialBodyParams,
403
+ path: pathParams,
404
+ query: queryParams,
405
+ };
406
+ }
407
+ /**
408
+ * wraps the emitted code emitted by setter in a "let Some" block
409
+ * if the parameter is optional, else the value of setter is returned.
410
+ *
411
+ * NOTE: for optional params, by convention, we'll create a local named param.name.
412
+ * setter MUST reference by param.name so it works for optional and required params.
413
+ *
414
+ * @param indent the indentation helper currently in scope
415
+ * @param param the parameter to which the contents of setter apply
416
+ * @param setter the callback that emits the code to read from a param var
417
+ * @returns
418
+ */
419
+ function getParamValueHelper(indent, param, setter) {
420
+ if (param.optional) {
421
+ // optional params are in the unwrapped options local var
422
+ const op = indent.get() + helpers.buildIfBlock(indent, {
423
+ condition: `let Some(${param.name}) = options.${param.name}`,
424
+ body: setter,
425
+ });
426
+ return op + '\n';
427
+ }
428
+ return setter();
429
+ }
430
+ /**
431
+ * emits the code for building the request URL.
432
+ * assumes that there's a mutable local var 'url'
433
+ *
434
+ * @param indent the indentation helper currently in scope
435
+ * @param use the use statement builder currently in scope
436
+ * @param method the method for which we're building the body
437
+ * @param paramGroups the param groups for the provided method
438
+ * @param fromSelf applicable for client params. when true, the prefix "self." is included
439
+ * @returns the URL construction code
440
+ */
441
+ function constructUrl(indent, use, method, paramGroups, fromSelf = true) {
442
+ // for paths that contain query parameters, we must set the query params separately.
443
+ // including them in the call to set_path() causes the chars to be path-escaped.
444
+ const pathChunks = method.httpPath.split('?');
445
+ if (pathChunks.length > 2) {
446
+ throw new Error('too many HTTP path chunks');
447
+ }
448
+ let body = '';
449
+ let path = `"${pathChunks[0]}"`;
450
+ if (paramGroups.path.length > 0) {
433
451
  // we have path params that need to have their segments replaced with the param values
434
- body += `${indentation.get()}let mut path = String::from(${path});\n`;
435
- for (const pathParam of pathParams) {
436
- body += `${indentation.get()}path = path.replace("{${pathParam.segment}}", &${getHeaderPathQueryParamValue(pathParam)});\n`;
452
+ body += `${indent.get()}let mut path = String::from(${path});\n`;
453
+ for (const pathParam of paramGroups.path) {
454
+ body += `${indent.get()}path = path.replace("{${pathParam.segment}}", &${getHeaderPathQueryParamValue(use, pathParam, fromSelf)});\n`;
437
455
  }
438
456
  path = '&path';
439
457
  }
440
- body += `${indentation.get()}url.set_path(${path});\n`;
441
- for (const queryParam of queryParams) {
458
+ body += `${indent.get()}url.set_path(${path});\n`;
459
+ if (pathChunks.length === 2) {
460
+ body += `${indent.get()}url.query_pairs_mut()`;
461
+ // set the query params that were in the path
462
+ const qps = queryString.parse(pathChunks[1]);
463
+ for (const qp of Object.keys(qps)) {
464
+ const val = qps[qp];
465
+ if (val) {
466
+ if (typeof val === 'string') {
467
+ body += `.append_pair("${qp}", "${val}")`;
468
+ }
469
+ else {
470
+ for (const v of val) {
471
+ body += `.append_pair("${qp}", "${v}")`;
472
+ }
473
+ }
474
+ }
475
+ else {
476
+ body += `.append_key_only("${qp}")`;
477
+ }
478
+ }
479
+ body += ';\n';
480
+ }
481
+ for (const queryParam of paramGroups.query) {
442
482
  if (queryParam.kind === 'queryCollection' && queryParam.format === 'multi') {
443
- const valueVar = queryParam.name[0];
444
- body += `${indentation.get()}for ${valueVar} in ${queryParam.name}.iter() {\n`;
445
- body += `${indentation.push().get()}url.query_pairs_mut().append_pair("${queryParam.key}", ${valueVar});\n`;
446
- body += `${indentation.pop().get()}}\n`;
483
+ body += getParamValueHelper(indent, queryParam, () => {
484
+ const valueVar = queryParam.name[0];
485
+ let text = `${indent.get()}for ${valueVar} in ${queryParam.name}.iter() {\n`;
486
+ text += `${indent.push().get()}url.query_pairs_mut().append_pair("${queryParam.key}", ${valueVar});\n`;
487
+ text += `${indent.pop().get()}}\n`;
488
+ return text;
489
+ });
447
490
  }
448
491
  else {
449
- body += `${indentation.get()}url.query_pairs_mut().append_pair("${queryParam.key}", &${getHeaderPathQueryParamValue(queryParam)});\n`;
492
+ body += getParamValueHelper(indent, queryParam, () => {
493
+ return `${indent.get()}url.query_pairs_mut().append_pair("${queryParam.key}", &${getHeaderPathQueryParamValue(use, queryParam, fromSelf)});\n`;
494
+ });
450
495
  }
451
496
  }
452
- body += `${indentation.get()}let mut request = Request::new(url, Method::${codegen.capitalize(method.httpMethod)});\n`;
453
- for (const headerParam of headerParams) {
454
- body += `${indentation.get()}request.insert_header("${headerParam.header.toLowerCase()}", ${getHeaderPathQueryParamValue(headerParam)});\n`;
497
+ return body;
498
+ }
499
+ /**
500
+ * emits the code for building the HTTP request.
501
+ * assumes that there's a local var 'url' which is the Url.
502
+ * creates a mutable local 'request' which is the Request instance.
503
+ *
504
+ * @param indent the indentation helper currently in scope
505
+ * @param use the use statement builder currently in scope
506
+ * @param method the method for which we're building the body
507
+ * @param paramGroups the param groups for the provided method
508
+ * @param fromSelf applicable for client params. when true, the prefix "self." is included
509
+ * @returns the request construction code
510
+ */
511
+ function constructRequest(indent, use, method, paramGroups, fromSelf = true) {
512
+ let body = `${indent.get()}let mut request = Request::new(url, Method::${codegen.capitalize(method.httpMethod)});\n`;
513
+ for (const headerParam of paramGroups.header) {
514
+ body += getParamValueHelper(indent, headerParam, () => {
515
+ if (headerParam.kind === 'headerHashMap') {
516
+ let setter = `for (k, v) in &${headerParam.name} {\n`;
517
+ setter += `${indent.push().get()}request.insert_header(format!("${headerParam.header}-{}", k), v);\n`;
518
+ setter += `${indent.pop().get()}}\n`;
519
+ return setter;
520
+ }
521
+ return `${indent.get()}request.insert_header("${headerParam.header.toLowerCase()}", ${getHeaderPathQueryParamValue(use, headerParam, fromSelf)});\n`;
522
+ });
455
523
  }
456
- const bodyParam = getBodyParameter(method);
524
+ const bodyParam = paramGroups.body;
457
525
  if (bodyParam) {
458
- body += `${indentation.get()}request.set_body(${bodyParam.name});\n`;
526
+ body += getParamValueHelper(indent, bodyParam, () => {
527
+ return `${indent.get()}request.set_body(${bodyParam.name});\n`;
528
+ });
529
+ }
530
+ else if (paramGroups.partialBody.length > 0) {
531
+ // all partial body params should point to the same underlying model type.
532
+ const requestContentType = paramGroups.partialBody[0].type;
533
+ use.addForType(requestContentType);
534
+ body += `${indent.get()}let body: ${helpers.getTypeDeclaration(requestContentType)} = ${requestContentType.type.name} {\n`;
535
+ indent.push();
536
+ for (const partialBodyParam of paramGroups.partialBody) {
537
+ if (partialBodyParam.type.type !== requestContentType.type) {
538
+ throw new Error(`spread param ${partialBodyParam.name} has conflicting model type ${partialBodyParam.type.type.name}, expected model type ${requestContentType.type.name}`);
539
+ }
540
+ let initializer = partialBodyParam.name;
541
+ if (partialBodyParam.optional) {
542
+ initializer = `${partialBodyParam.name}: options.${partialBodyParam.name}`;
543
+ }
544
+ else if (!requestContentType.type.internal) {
545
+ // spread param maps to a non-internal model, so it must be wrapped in Some()
546
+ initializer = `${partialBodyParam.name}: Some(${partialBodyParam.name})`;
547
+ }
548
+ body += `${indent.get()}${initializer},\n`;
549
+ }
550
+ body += `${indent.pop().get()}}.try_into()?;\n`;
551
+ body += `${indent.get()}request.set_body(body);\n`;
459
552
  }
460
- body += `${indentation.get()}self.pipeline.send(&mut ctx, &mut request).await\n`;
461
553
  return body;
462
554
  }
463
- function getBodyParameter(method) {
464
- let bodyParam;
555
+ /**
556
+ * constructs the body for an async client method
557
+ *
558
+ * @param indent the indentation helper currently in scope
559
+ * @param use the use statement builder currently in scope
560
+ * @param client the client to which the method belongs
561
+ * @param method the method for the body to build
562
+ * @returns the contents of the method body
563
+ */
564
+ function getAsyncMethodBody(indent, use, client, method) {
565
+ use.addTypes('azure_core', ['AsClientMethodOptions', 'Method', 'Request']);
566
+ let body = 'let options = options.unwrap_or_default();\n';
567
+ body += `${indent.get()}let mut ctx = options.method_options.context();\n`;
568
+ body += `${indent.get()}let mut url = self.${getEndpointFieldName(client)}.clone();\n`;
569
+ const paramGroups = getMethodParamGroup(method);
570
+ body += constructUrl(indent, use, method, paramGroups);
571
+ body += constructRequest(indent, use, method, paramGroups);
572
+ body += `${indent.get()}self.pipeline.send(&mut ctx, &mut request).await\n`;
573
+ return body;
574
+ }
575
+ /**
576
+ * constructs the body for a pageable client method
577
+ *
578
+ * @param indent the indentation helper currently in scope
579
+ * @param use the use statement builder currently in scope
580
+ * @param client the client to which the method belongs
581
+ * @param method the method for the body to build
582
+ * @returns the contents of the method body
583
+ */
584
+ function getPageableMethodBody(indent, use, client, method) {
585
+ if (!method.strategy) {
586
+ throw new Error('paged method with no strategy NYI');
587
+ }
588
+ else if (method.strategy.kind !== 'nextLink') {
589
+ throw new Error('paged methods other than nextLink NYI');
590
+ }
591
+ const nextLinkName = method.strategy.nextLinkName;
592
+ use.addTypes('azure_core', ['Method', 'Pager', 'Request', 'Response', 'Result', 'Url']);
593
+ use.addType('typespec_client_core::http', 'PagerResult');
594
+ use.addType('typespec_client_core', 'json');
595
+ use.addForType(method.returns.type.type);
596
+ const paramGroups = getMethodParamGroup(method);
597
+ let body = 'let options = options.unwrap_or_default().into_owned();\n';
598
+ body += `${indent.get()}let endpoint = self.${getEndpointFieldName(client)}.clone();\n`;
599
+ body += `${indent.get()}let pipeline = self.pipeline.clone();\n`;
600
+ // clone any other client params
465
601
  for (const param of method.params) {
466
- if (param.kind === 'body') {
467
- if (bodyParam) {
468
- throw new Error(`method ${method.name} has multiple body parameters`);
469
- }
470
- bodyParam = param;
602
+ if (param.location === 'client') {
603
+ body += `${indent.get()}let ${param.name} = self.${param.name}.clone();\n`;
471
604
  }
472
605
  }
473
- return bodyParam;
606
+ body += `${indent.get()}Ok(Pager::from_callback(move |${nextLinkName}: Option<Url>| {\n`;
607
+ body += `${indent.push().get()}let mut url: Url;\n`;
608
+ body += indent.get() + helpers.buildMatch(indent, nextLinkName, [{
609
+ pattern: `Some(${nextLinkName})`,
610
+ body: (indent) => `${indent.get()}url = ${nextLinkName};\n`
611
+ }, {
612
+ pattern: 'None',
613
+ body: (indent) => {
614
+ let none = `${indent.get()}url = endpoint.clone();\n`;
615
+ none += constructUrl(indent, use, method, paramGroups, false);
616
+ return none;
617
+ }
618
+ }]);
619
+ body += ';\n';
620
+ // here we want the T in Pager<T>
621
+ const returnType = helpers.getTypeDeclaration(method.returns.type.type);
622
+ body += constructRequest(indent, use, method, paramGroups, false);
623
+ body += `${indent.get()}let mut ctx = options.method_options.context.clone();\n`;
624
+ body += `${indent.get()}let pipeline = pipeline.clone();\n`;
625
+ body += `${indent.get()}async move {\n`;
626
+ body += `${indent.push().get()}let rsp: Response<${returnType}> = pipeline.send(&mut ctx, &mut request).await?;\n`;
627
+ body += `${indent.get()}let (status, headers, body) = rsp.deconstruct();\n`;
628
+ body += `${indent.get()}let bytes = body.collect().await?;\n`;
629
+ body += `${indent.get()}let res: ${returnType} = json::from_json(bytes.clone())?;\n`;
630
+ body += `${indent.get()}let rsp = Response::from_bytes(status, headers, bytes);\n`;
631
+ body += `${indent.get()}Ok(${helpers.buildMatch(indent, `res.${nextLinkName}`, [{
632
+ pattern: `Some(${nextLinkName})`,
633
+ returns: 'PagerResult::Continue',
634
+ body: (indent) => `${indent.get()}response: (rsp),\n${indent.get()}continuation: (${nextLinkName}),\n`
635
+ }, {
636
+ pattern: 'None',
637
+ returns: 'PagerResult::Complete',
638
+ body: () => `${indent.get()}response: (rsp),\n`
639
+ }])}`;
640
+ body += ')\n'; // end Ok
641
+ body += `${indent.pop().get()}}\n`; // end async move
642
+ body += `${indent.pop().get()}}))`; // end Ok/Pager::from_callback
643
+ return body;
474
644
  }
475
- function getHeaderPathQueryParamValue(param) {
645
+ /**
646
+ * contains the code to use when populating a header/path/query value
647
+ * from a parameter of that type.
648
+ *
649
+ * if the param's type is a String, then the return value is simply the
650
+ * param's name. the non-String cases require some kind of conversion.
651
+ * this could simply be a to_string() call, e.g. "paramName.to_string()".
652
+ * other cases might be more complex.
653
+ *
654
+ * @param use the use statement builder currently in scope
655
+ * @param param the param for which to get the value
656
+ * @param fromSelf applicable for client params. when true, the prefix "self." is included
657
+ * @returns
658
+ */
659
+ function getHeaderPathQueryParamValue(use, param, fromSelf = true) {
476
660
  let paramName = '';
477
- if (param.location === 'client') {
661
+ // when fromSelf is false we assume that there's a local with the same name.
662
+ // e.g. in pageable methods where we need to clone the params so they can be
663
+ // passed to a future that can outlive the calling method.
664
+ if (param.location === 'client' && fromSelf) {
478
665
  paramName = 'self.';
479
666
  }
667
+ const encodeBytes = function (type, param) {
668
+ use.addType('azure_core', 'base64');
669
+ let encoding;
670
+ switch (type.encoding) {
671
+ case 'std':
672
+ encoding = 'base64::encode';
673
+ break;
674
+ case 'url':
675
+ encoding = 'base64::encode_url_safe';
676
+ break;
677
+ }
678
+ return `${encoding}(${param})`;
679
+ };
480
680
  if (param.kind === 'headerCollection' || param.kind === 'queryCollection') {
481
681
  if (param.format === 'multi') {
482
682
  throw new Error('multi should have been handled outside getHeaderPathQueryParamValue');
483
683
  }
484
- return `${param.name}.join("${getCollectionDelimiter(param.format)}")`;
684
+ else if (param.type.type.kind === 'String') {
685
+ return `${param.name}.join("${getCollectionDelimiter(param.format)}")`;
686
+ }
687
+ // convert the items to strings
688
+ let strConv = 'i.to_string()';
689
+ if (param.type.type.kind === 'encodedBytes') {
690
+ strConv = encodeBytes(param.type.type, 'i');
691
+ }
692
+ return `${param.name}.iter().map(|i| ${strConv}).collect::<Vec<String>>().join("${getCollectionDelimiter(param.format)}")`;
485
693
  }
486
694
  switch (param.type.kind) {
487
695
  case 'String':
488
696
  paramName += param.name;
489
697
  break;
698
+ case 'encodedBytes':
699
+ return encodeBytes(param.type, param.name);
700
+ case 'enum':
701
+ case 'hashmap':
702
+ case 'offsetDateTime':
703
+ case 'scalar':
704
+ paramName += `${param.name}.to_string()`;
705
+ break;
490
706
  case 'implTrait':
491
707
  // only done for method params so no need to include paramName prefix
492
708
  return `${param.name}.into()`;
@@ -497,6 +713,12 @@ function getHeaderPathQueryParamValue(param) {
497
713
  }
498
714
  return paramName;
499
715
  }
716
+ /**
717
+ * returns the delimiter character for the provided format type
718
+ *
719
+ * @param format the format collection type
720
+ * @returns the delimiter character
721
+ */
500
722
  function getCollectionDelimiter(format) {
501
723
  switch (format) {
502
724
  case 'csv':
@@ -507,8 +729,19 @@ function getCollectionDelimiter(format) {
507
729
  return ' ';
508
730
  case 'tsv':
509
731
  return '\t';
510
- default:
511
- throw new Error(`unhandled collection format ${format}`);
512
732
  }
513
733
  }
734
+ /**
735
+ * returns the lifetime annotation for the provide struct.
736
+ * can return the empty string if the struct has no lifetime.
737
+ *
738
+ * @param type the struct for which to get the annotation
739
+ * @returns the annotation or empty string
740
+ */
741
+ function getLifetimeAnnotation(type) {
742
+ if (type.lifetime) {
743
+ return `${helpers.getGenericLifetimeAnnotation(type.lifetime)}`;
744
+ }
745
+ return '';
746
+ }
514
747
  //# sourceMappingURL=clients.js.map