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