@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.
- 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 +489 -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 +36 -23
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.d.ts +2 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -1
- 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 +342 -81
- 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,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 += `${
|
|
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';
|
|
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
|
-
|
|
89
|
-
let 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
|
-
|
|
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 += `${
|
|
111
|
-
body += `${
|
|
112
|
-
body += `${
|
|
113
|
-
body += `${
|
|
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 += `${
|
|
131
|
-
body += `${
|
|
132
|
-
|
|
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 += `${
|
|
147
|
+
body += `${indent.get()}${field.name}: ${field.defaultValue},\n`;
|
|
138
148
|
}
|
|
139
|
-
body += `${
|
|
140
|
-
body += `${
|
|
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 += `${
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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.
|
|
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.
|
|
236
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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 += `${
|
|
400
|
-
body += `${
|
|
401
|
-
body += `${
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
|
432
|
-
|
|
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 += `${
|
|
435
|
-
for (const pathParam of
|
|
436
|
-
body += `${
|
|
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 += `${
|
|
441
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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 +=
|
|
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
|
|
453
|
-
|
|
454
|
-
|
|
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 =
|
|
530
|
+
const bodyParam = paramGroups.body;
|
|
457
531
|
if (bodyParam) {
|
|
458
|
-
body +=
|
|
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
|
-
|
|
464
|
-
|
|
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.
|
|
467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|