@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.
- package/dist/src/codegen/cargotoml.d.ts +6 -0
- package/dist/src/codegen/cargotoml.d.ts.map +1 -1
- package/dist/src/codegen/cargotoml.js +6 -1
- package/dist/src/codegen/cargotoml.js.map +1 -1
- package/dist/src/codegen/clients.d.ts +19 -2
- package/dist/src/codegen/clients.d.ts.map +1 -1
- package/dist/src/codegen/clients.js +483 -250
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/codeGenerator.d.ts +29 -6
- package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
- package/dist/src/codegen/codeGenerator.js +64 -25
- package/dist/src/codegen/codeGenerator.js.map +1 -1
- package/dist/src/codegen/context.d.ts +41 -0
- package/dist/src/codegen/context.d.ts.map +1 -1
- package/dist/src/codegen/context.js +94 -15
- package/dist/src/codegen/context.js.map +1 -1
- package/dist/src/codegen/enums.d.ts +9 -1
- package/dist/src/codegen/enums.d.ts.map +1 -1
- package/dist/src/codegen/enums.js +34 -20
- package/dist/src/codegen/enums.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts +116 -2
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +148 -18
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/lib.d.ts +7 -1
- package/dist/src/codegen/lib.d.ts.map +1 -1
- package/dist/src/codegen/lib.js +10 -5
- package/dist/src/codegen/lib.js.map +1 -1
- package/dist/src/codegen/mod.d.ts +6 -1
- package/dist/src/codegen/mod.d.ts.map +1 -1
- package/dist/src/codegen/mod.js +9 -10
- package/dist/src/codegen/mod.js.map +1 -1
- package/dist/src/codegen/models.d.ts +20 -1
- package/dist/src/codegen/models.d.ts.map +1 -1
- package/dist/src/codegen/models.js +250 -13
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/use.d.ts +32 -0
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +42 -15
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +150 -11
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +60 -17
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/crate.d.ts +33 -0
- package/dist/src/codemodel/crate.d.ts.map +1 -1
- package/dist/src/codemodel/crate.js +17 -0
- package/dist/src/codemodel/crate.js.map +1 -1
- package/dist/src/codemodel/method.d.ts +17 -0
- package/dist/src/codemodel/method.d.ts.map +1 -1
- package/dist/src/codemodel/types.d.ts +152 -24
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +39 -25
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts +5 -0
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +20 -20
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.d.ts +1 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +1 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +73 -1
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +338 -71
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/tcgcadapter/helpers.d.ts +18 -0
- package/dist/src/tcgcadapter/helpers.d.ts.map +1 -1
- package/dist/src/tcgcadapter/helpers.js +23 -2
- package/dist/src/tcgcadapter/helpers.js.map +1 -1
- package/dist/src/tcgcadapter/naming.d.ts +8 -0
- package/dist/src/tcgcadapter/naming.d.ts.map +1 -1
- package/dist/src/tcgcadapter/naming.js +8 -5
- package/dist/src/tcgcadapter/naming.js.map +1 -1
- 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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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
|
|
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(
|
|
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 += `${
|
|
35
|
+
body += `${indent.get()}${pubInClients}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n`;
|
|
28
36
|
}
|
|
29
37
|
use.addType('azure_core', 'Pipeline');
|
|
30
|
-
body += `${
|
|
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 += `${
|
|
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 += `${
|
|
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 += `${
|
|
57
|
-
body += `${
|
|
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 += `${
|
|
69
|
+
body += `${indent.get()}${authPolicy}\n`;
|
|
62
70
|
}
|
|
63
|
-
body += `${
|
|
64
|
-
body += `${
|
|
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
|
-
|
|
74
|
+
indent.push();
|
|
67
75
|
for (const field of getClientOptionsFields(client.constructable.options)) {
|
|
68
|
-
body += `${
|
|
76
|
+
body += `${indent.get()}${field.name}: options.${field.name},\n`;
|
|
69
77
|
}
|
|
70
|
-
body += `${
|
|
71
|
-
body += `${
|
|
72
|
-
body += `${
|
|
73
|
-
body += `${
|
|
74
|
-
body += `${
|
|
75
|
-
body += `${
|
|
76
|
-
body += `${
|
|
77
|
-
body += `${
|
|
78
|
-
body += `${
|
|
79
|
-
body += `${
|
|
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
|
-
|
|
89
|
-
let 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
|
-
|
|
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 += `${
|
|
111
|
-
body += `${
|
|
112
|
-
body += `${
|
|
113
|
-
body += `${
|
|
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 += `${
|
|
131
|
-
body += `${
|
|
132
|
-
|
|
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 += `${
|
|
141
|
+
body += `${indent.get()}${field.name}: ${field.defaultValue},\n`;
|
|
138
142
|
}
|
|
139
|
-
body += `${
|
|
140
|
-
body += `${
|
|
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 += `${
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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.
|
|
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.
|
|
236
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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 += `${
|
|
400
|
-
body += `${
|
|
401
|
-
body += `${
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
|
432
|
-
|
|
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 += `${
|
|
435
|
-
for (const pathParam of
|
|
436
|
-
body += `${
|
|
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 += `${
|
|
441
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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 +=
|
|
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
|
|
453
|
-
|
|
454
|
-
|
|
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 =
|
|
524
|
+
const bodyParam = paramGroups.body;
|
|
457
525
|
if (bodyParam) {
|
|
458
|
-
body +=
|
|
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
|
-
|
|
464
|
-
|
|
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.
|
|
467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|