vovk-rust 0.0.1-draft.31

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.
@@ -0,0 +1,83 @@
1
+ <%
2
+ const data = {
3
+ package: {
4
+ name: t.package.name.replace(/-/g, '_'),
5
+ version: t.package.version,
6
+ edition: "2021"
7
+ },
8
+ dependencies: {
9
+ serde: { version: "1.0", features: ["derive"] },
10
+ serde_json: "1.0",
11
+ reqwest: { version: "0.12", features: ["blocking", "json", "multipart"] },
12
+ jsonschema: "0.17",
13
+ urlencoding: "2.1",
14
+ once_cell: "1.17"
15
+ }
16
+ };
17
+
18
+ // Add optional fields to package section
19
+ if (t.package.description) {
20
+ data.package.description = t.package.description;
21
+ }
22
+
23
+ if (t.package.license) {
24
+ data.package.license = t.package.license || 'UNLICENSED';
25
+ }
26
+
27
+ if (t.package.repository) {
28
+ data.package.repository = typeof t.package.repository === 'string'
29
+ ? t.package.repository
30
+ : t.package.repository.url;
31
+ }
32
+
33
+ if (t.package.homepage) {
34
+ data.package.homepage = t.package.homepage;
35
+ }
36
+
37
+ // Build authors array
38
+ const authors = [];
39
+ if (t.package.author) {
40
+ if (typeof t.package.author === 'string') {
41
+ authors.push(t.package.author);
42
+ } else {
43
+ let author = t.package.author.name;
44
+ if (t.package.author.email) author += ` <${t.package.author.email}>`;
45
+ authors.push(author);
46
+ }
47
+ }
48
+
49
+ if (t.package.contributors && t.package.contributors.length) {
50
+ t.package.contributors.forEach(contributor => {
51
+ if (typeof contributor === 'string') {
52
+ authors.push(contributor);
53
+ } else {
54
+ let contribStr = contributor.name;
55
+ if (contributor.email) contribStr += ` <${contributor.email}>`;
56
+ authors.push(contribStr);
57
+ }
58
+ });
59
+ }
60
+
61
+ if (authors.length) {
62
+ data.package.authors = authors;
63
+ }
64
+
65
+ // Add keywords if they exist
66
+ if (t.package.keywords && t.package.keywords.length) {
67
+ data.package.keywords = t.package.keywords;
68
+ }
69
+
70
+ // Add bugs if they exist
71
+ if (t.package.bugs) {
72
+ if (!data.package.metadata) {
73
+ data.package.metadata = {};
74
+ }
75
+
76
+ data.package.metadata.package = {
77
+ bugs: typeof t.package.bugs === 'string'
78
+ ? t.package.bugs
79
+ : t.package.bugs.url
80
+ };
81
+ }
82
+ %>
83
+ <%- t.TOML.stringify(data) %>
@@ -0,0 +1,35 @@
1
+ # <%= t.package.name.replace(/-/g, '_') %> v<%= t.package.version %> ![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)
2
+
3
+ <%- t.package.description ? `> ${t.package.description}` : '' %>
4
+
5
+ <%- t.package.license ? `License: **${t.package.license}**` : '' %>
6
+
7
+
8
+ ```bash
9
+ # Install the package
10
+ cargo install <%= t.package.name.replace(/-/g, '_') %>
11
+ ```
12
+
13
+ <% Object.entries(t.schema.segments).forEach(([segmentName, segment]) => {
14
+ Object.values(segment.controllers).forEach((controllerSchema) => { %>
15
+
16
+ ## mod <%= t._.snakeCase(controllerSchema.rpcModuleName) %>
17
+ <% Object.entries(controllerSchema.handlers).forEach(([handlerName, handlerSchema]) => { %>
18
+ ### <%= t._.snakeCase(controllerSchema.rpcModuleName) %>::<%= t._.snakeCase(handlerName) %>
19
+ <%- handlerSchema.openapi?.summary ? `> ${handlerSchema.openapi.summary}` : '' %>
20
+
21
+ <%- handlerSchema.openapi?.description ? `${handlerSchema.openapi.description}` : '' %>
22
+
23
+ `<%= handlerSchema.httpMethod %> <%= [t.apiRoot, segmentName, controllerSchema.prefix, handlerSchema.path].filter(Boolean).join('/') %>`
24
+
25
+ ```rust
26
+ <%- t.createCodeExamples({
27
+ handlerSchema,
28
+ handlerName,
29
+ controllerSchema,
30
+ package: t.package,
31
+ }).rs %>
32
+ ```
33
+ <% }) %>
34
+ <% }) %>
35
+ <% }) %>
@@ -0,0 +1,488 @@
1
+ use serde::{Serialize, de::DeserializeOwned};
2
+ use reqwest::blocking::Client;
3
+ use reqwest::Method;
4
+ use reqwest::blocking::multipart;
5
+ use core::panic;
6
+ use std::collections::HashMap;
7
+ use std::error::Error;
8
+ use std::fmt;
9
+ use jsonschema::JSONSchema;
10
+ use serde_json::Value;
11
+ use urlencoding;
12
+ use crate::read_full_schema;
13
+ use once_cell::sync::Lazy;
14
+
15
+ // Custom error type for HTTP exceptions
16
+ #[derive(Debug, Serialize)]
17
+ pub struct HttpException {
18
+ message: String,
19
+ status_code: i32,
20
+ #[allow(dead_code)]
21
+ cause: Option<Value>,
22
+ }
23
+
24
+ impl fmt::Display for HttpException {
25
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26
+ write!(f, "[Status: {}] {}", self.status_code, self.message)
27
+ }
28
+ }
29
+
30
+ impl Error for HttpException {}
31
+
32
+ // Load the full schema only once using lazy initialization
33
+ static FULL_SCHEMA: Lazy<Result<Value, String>> = Lazy::new(|| {
34
+ read_full_schema::read_full_schema()
35
+ .map(|schema| serde_json::to_value(schema).expect("Failed to convert schema to Value"))
36
+ .map_err(|e| format!("Failed to read schema: {}", e))
37
+ });
38
+
39
+ // Private helper function for request preparation
40
+ fn prepare_request<B, Q, P>(
41
+ default_api_root: &str,
42
+ segment_name: &str,
43
+ controller_name: &str,
44
+ handler_name: &str,
45
+ body: Option<&B>,
46
+ form: Option<multipart::Form>,
47
+ query: Option<&Q>,
48
+ params: Option<&P>,
49
+ headers: Option<&HashMap<String, String>>,
50
+ api_root: Option<&str>,
51
+ disable_client_validation: bool,
52
+ ) -> Result<(reqwest::blocking::RequestBuilder, String), Box<dyn Error>>
53
+ where
54
+ B: Serialize + ?Sized,
55
+ Q: Serialize + ?Sized,
56
+ P: Serialize + ?Sized,
57
+ {
58
+ // Extract schema information
59
+ let schema = match &*FULL_SCHEMA {
60
+ Ok(schema) => schema,
61
+ Err(e) => return Err(format!("Failed to load schema: {}", e).into()),
62
+ };
63
+
64
+ let segment = schema.get("segments")
65
+ .and_then(|s| s.get(segment_name))
66
+ .ok_or("Segment not found")?;
67
+
68
+ let controller = segment.get("controllers")
69
+ .and_then(|c| c.get(controller_name))
70
+ .ok_or("Controller not found")?;
71
+
72
+ let handlers = controller.get("handlers")
73
+ .and_then(|h| h.as_object())
74
+ .ok_or("Handlers not found")?;
75
+
76
+ let handler = handlers.get(handler_name).ok_or("Handler not found")?;
77
+ let prefix = controller.get("prefix")
78
+ .and_then(|p| p.as_str())
79
+ .ok_or("Prefix not found")?;
80
+ let handler_path = handler.get("path")
81
+ .and_then(|p| p.as_str())
82
+ .ok_or("Path not found")?;
83
+ let http_method = handler.get("httpMethod")
84
+ .ok_or("HTTP method not found")?
85
+ .as_str()
86
+ .ok_or("HTTP method is not a string")?;
87
+ let default_validation = Value::Object(serde_json::Map::new());
88
+ let validation = handler
89
+ .get("validation")
90
+ .unwrap_or(&default_validation);
91
+
92
+ // Construct the base URL
93
+ let url_parts: Vec<&str> = vec![api_root.unwrap_or(default_api_root), segment_name, prefix, handler_path]
94
+ .into_iter()
95
+ .filter(|s| !s.is_empty())
96
+ .collect();
97
+ let mut url = url_parts.join("/");
98
+
99
+ // Convert generic types to Value for validation if needed
100
+ let body_value = body.map(|b| serde_json::to_value(b))
101
+ .transpose()
102
+ .map_err(|e| format!("Failed to serialize body: {}", e))?;
103
+
104
+ let query_value = query.map(|q| serde_json::to_value(q))
105
+ .transpose()
106
+ .map_err(|e| format!("Failed to serialize query: {}", e))?;
107
+
108
+ let params_value = params.map(|p| serde_json::to_value(p))
109
+ .transpose()
110
+ .map_err(|e| format!("Failed to serialize params: {}", e))?;
111
+
112
+ // Perform JSON validation if not disabled and no form data is provided
113
+ if !disable_client_validation && form.is_none() {
114
+ if let Some(body_schema) = validation.get("body") {
115
+ if let Some(ref body_val) = body_value {
116
+ let schema =
117
+ JSONSchema::compile(body_schema).map_err(|e| format!("Invalid body schema: {}", e))?;
118
+ schema
119
+ .validate(body_val)
120
+ .map_err(|e| {
121
+ let error_msgs: Vec<String> = e.map(|err| format!("{}: {}", err.instance_path, err.to_string())).collect();
122
+ format!("Body validation failed: {}", error_msgs.join(", "))
123
+ })?;
124
+ } else if http_method != "GET" {
125
+ return Err("Body is required for validation but not provided".into());
126
+ }
127
+ }
128
+
129
+ if let Some(query_schema) = validation.get("query") {
130
+ if let Some(ref query_val) = query_value {
131
+ let schema =
132
+ JSONSchema::compile(query_schema).map_err(|e| format!("Invalid query schema: {}", e))?;
133
+ schema
134
+ .validate(query_val)
135
+ .map_err(|e| {
136
+ let error_msgs: Vec<String> = e.map(|err| format!("{}: {}", err.instance_path, err.to_string())).collect();
137
+ format!("Query validation failed: {}", error_msgs.join(", "))
138
+ })?;
139
+ } else {
140
+ return Err("Query is required for validation but not provided".into());
141
+ }
142
+ }
143
+
144
+ if let Some(params_schema) = validation.get("params") {
145
+ if let Some(ref params_val) = params_value {
146
+ let schema = JSONSchema::compile(params_schema)
147
+ .map_err(|e| format!("Invalid params schema: {}", e))?;
148
+ schema
149
+ .validate(params_val)
150
+ .map_err(|e| {
151
+ let error_msgs: Vec<String> = e.map(|err| format!("{}: {}", err.instance_path, err.to_string())).collect();
152
+ format!("Params validation failed: {}", error_msgs.join(", "))
153
+ })?;
154
+ } else {
155
+ return Err("Params are required for validation but not provided".into());
156
+ }
157
+ }
158
+ }
159
+
160
+ // Substitute path parameters in the URL
161
+ if let Some(ref params_val) = params_value {
162
+ if let Value::Object(map) = params_val {
163
+ for (key, value) in map {
164
+ let pattern = format!(":{}", key);
165
+ if let Value::String(s) = value {
166
+ url = url.replace(&pattern, s);
167
+ } else {
168
+ return Err(format!("Param {} must be a string", key).into());
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ // Append query string if query parameters are provided
175
+ if let Some(ref query_val) = query_value {
176
+ let query_string = build_query_string(query_val, "");
177
+ if !query_string.is_empty() {
178
+ if url.contains('?') {
179
+ url += "&";
180
+ } else {
181
+ url += "?";
182
+ }
183
+ url += &query_string;
184
+ }
185
+ }
186
+
187
+ // Set up request headers
188
+ let mut headers_map = reqwest::header::HeaderMap::new();
189
+ headers_map.insert("Accept", "application/jsonl, application/json".parse().unwrap());
190
+ if body_value.is_some() && form.is_none() {
191
+ headers_map.insert("Content-Type", "application/json".parse().unwrap());
192
+ }
193
+
194
+ // Merge with user-provided headers if any
195
+ if let Some(provided_headers) = headers {
196
+ for (key, value) in provided_headers {
197
+ if let Ok(header_name) = reqwest::header::HeaderName::from_bytes(key.as_bytes()) {
198
+ if let Ok(header_value) = reqwest::header::HeaderValue::from_str(value) {
199
+ headers_map.insert(header_name, header_value);
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ // Map HTTP method string to reqwest::Method
206
+ let method = match http_method.to_uppercase().as_str() {
207
+ "GET" => Method::GET,
208
+ "POST" => Method::POST,
209
+ "PUT" => Method::PUT,
210
+ "DELETE" => Method::DELETE,
211
+ "PATCH" => Method::PATCH,
212
+ "OPTIONS" => Method::OPTIONS,
213
+ "HEAD" => Method::HEAD,
214
+ _ => return Err("Invalid HTTP method".into()),
215
+ };
216
+
217
+ // Build the HTTP request
218
+ let client = Client::new();
219
+ let mut request = client.request(method, &url).headers(headers_map);
220
+
221
+ // Apply form data or JSON body to the request
222
+ if let Some(form_data) = form {
223
+ request = request.multipart(form_data);
224
+ } else if let Some(body_val) = body_value {
225
+ request = request.json(&body_val);
226
+ }
227
+
228
+ Ok((request, http_method.to_string()))
229
+ }
230
+
231
+ // Main request function for regular (non-streaming) responses
232
+ #[allow(dead_code)]
233
+ pub fn http_request<T, B, Q, P>(
234
+ default_api_root: &str,
235
+ segment_name: &str,
236
+ controller_name: &str,
237
+ handler_name: &str,
238
+ body: Option<&B>,
239
+ form: Option<multipart::Form>,
240
+ query: Option<&Q>,
241
+ params: Option<&P>,
242
+ headers: Option<&HashMap<String, String>>,
243
+ api_root: Option<&str>,
244
+ disable_client_validation: bool,
245
+ ) -> Result<T, HttpException>
246
+ where
247
+ T: DeserializeOwned + 'static,
248
+ B: Serialize + ?Sized,
249
+ Q: Serialize + ?Sized,
250
+ P: Serialize + ?Sized,
251
+ {
252
+ // Prepare the request using the helper function
253
+ let (request, _) = prepare_request(
254
+ default_api_root,
255
+ segment_name,
256
+ controller_name,
257
+ handler_name,
258
+ body,
259
+ form,
260
+ query,
261
+ params,
262
+ headers,
263
+ api_root,
264
+ disable_client_validation,
265
+ ).map_err(|e| HttpException {
266
+ message: e.to_string(),
267
+ status_code: 0,
268
+ cause: None,
269
+ })?;
270
+
271
+ // Send the request
272
+ let response = request.send().map_err(|e| HttpException {
273
+ message: e.to_string(),
274
+ status_code: 0,
275
+ cause: None,
276
+ })?;
277
+
278
+ // Handle the response based on Content-Type
279
+ let content_type = response
280
+ .headers()
281
+ .get("Content-Type")
282
+ .and_then(|v| v.to_str().ok());
283
+
284
+ match content_type {
285
+ Some(ct) if ct.contains("application/json") => {
286
+ let value: Value = response.json().map_err(|e| HttpException {
287
+ message: e.to_string(),
288
+ status_code: 0,
289
+ cause: None,
290
+ })?;
291
+ if value.get("isError").is_some() {
292
+ let message = value["message"]
293
+ .as_str()
294
+ .unwrap_or("Unknown error")
295
+ .to_string();
296
+ let status_code = value["statusCode"].as_i64().unwrap_or(0) as i32;
297
+ let cause = value.get("cause").cloned();
298
+ return Err(HttpException {
299
+ message,
300
+ status_code,
301
+ cause,
302
+ });
303
+ }
304
+
305
+ let typed_value = serde_json::from_value::<T>(value).map_err(|e| HttpException {
306
+ message: e.to_string(),
307
+ status_code: 0,
308
+ cause: None,
309
+ })?;
310
+ Ok(typed_value)
311
+ }
312
+ _ => {
313
+ let text = response.text().map_err(|e| HttpException {
314
+ message: e.to_string(),
315
+ status_code: 0,
316
+ cause: None,
317
+ })?;
318
+ let typed_value = serde_json::from_str::<T>(&text).map_err(|e| HttpException {
319
+ message: e.to_string(),
320
+ status_code: 0,
321
+ cause: None,
322
+ })?;
323
+ Ok(typed_value)
324
+ }
325
+ }
326
+ }
327
+
328
+ // Request function specifically for streaming responses
329
+ #[allow(dead_code)]
330
+ pub fn http_request_stream<T, B, Q, P>(
331
+ default_api_root: &str,
332
+ segment_name: &str,
333
+ controller_name: &str,
334
+ handler_name: &str,
335
+ body: Option<&B>,
336
+ form: Option<multipart::Form>,
337
+ query: Option<&Q>,
338
+ params: Option<&P>,
339
+ headers: Option<&HashMap<String, String>>,
340
+ api_root: Option<&str>,
341
+ disable_client_validation: bool,
342
+ ) -> Result<Box<dyn Iterator<Item = T>>, HttpException>
343
+ where
344
+ T: DeserializeOwned + 'static,
345
+ B: Serialize + ?Sized,
346
+ Q: Serialize + ?Sized,
347
+ P: Serialize + ?Sized,
348
+ {
349
+ // Prepare the request using the helper function
350
+ let (request, _) = prepare_request(
351
+ default_api_root,
352
+ segment_name,
353
+ controller_name,
354
+ handler_name,
355
+ body,
356
+ form,
357
+ query,
358
+ params,
359
+ headers,
360
+ api_root,
361
+ disable_client_validation,
362
+ ).map_err(|e| HttpException {
363
+ message: e.to_string(),
364
+ status_code: 0,
365
+ cause: None,
366
+ })?;
367
+
368
+ // Send the request
369
+ let response = request.send().map_err(|e| HttpException {
370
+ message: e.to_string(),
371
+ status_code: 0,
372
+ cause: None,
373
+ })?;
374
+
375
+ // Create the streaming iterator
376
+ let json_stream = JsonlStream {
377
+ reader: std::io::BufReader::new(response),
378
+ buffer: String::new(),
379
+ };
380
+
381
+ let typed_stream = json_stream.map(|result| {
382
+ match result {
383
+ Ok(value) => {
384
+ if value.get("isError").is_some() {
385
+ let message = value["message"]
386
+ .as_str()
387
+ .unwrap_or("Unknown error")
388
+ .to_string();
389
+ panic!("Error from server: {}", message);
390
+ } else {
391
+ match serde_json::from_value::<T>(value) {
392
+ Ok(typed_value) => typed_value,
393
+ Err(e) => panic!("Failed to deserialize value: {}", e),
394
+ }
395
+ }
396
+ },
397
+ Err(e) => {
398
+ panic!("Error reading from stream: {}", e);
399
+ }
400
+ }
401
+ });
402
+
403
+ Ok(Box::new(typed_stream))
404
+ }
405
+
406
+ // Helper function to build query strings from nested JSON
407
+ fn build_query_string(data: &Value, prefix: &str) -> String {
408
+ match data {
409
+ Value::Object(map) => {
410
+ let parts: Vec<String> = map
411
+ .iter()
412
+ .map(|(k, v)| {
413
+ let new_prefix = if prefix.is_empty() {
414
+ k.to_string()
415
+ } else {
416
+ format!("{}[{}]", prefix, k)
417
+ };
418
+ build_query_string(v, &new_prefix)
419
+ })
420
+ .collect();
421
+ parts.join("&")
422
+ }
423
+ Value::Array(arr) => {
424
+ let parts: Vec<String> = arr
425
+ .iter()
426
+ .enumerate()
427
+ .map(|(i, v)| {
428
+ let new_prefix = format!("{}[{}]", prefix, i);
429
+ build_query_string(v, &new_prefix)
430
+ })
431
+ .collect();
432
+ parts.join("&")
433
+ }
434
+ Value::Null => String::new(),
435
+ _ => {
436
+ let value_str = match data {
437
+ Value::String(s) => s.clone(),
438
+ _ => data.to_string(),
439
+ };
440
+ format!("{}={}", prefix, urlencoding::encode(&value_str))
441
+ }
442
+ }
443
+ }
444
+
445
+ // Struct and Iterator implementation for streaming JSONL responses
446
+ struct JsonlStream {
447
+ reader: std::io::BufReader<reqwest::blocking::Response>,
448
+ buffer: String,
449
+ }
450
+
451
+ impl Iterator for JsonlStream {
452
+ type Item = Result<Value, Box<dyn Error>>;
453
+
454
+ fn next(&mut self) -> Option<Self::Item> {
455
+ use std::io::BufRead;
456
+
457
+ self.buffer.clear();
458
+ match self.reader.read_line(&mut self.buffer) {
459
+ Ok(0) => None, // End of stream
460
+ Ok(_) => {
461
+ let line = self.buffer.trim();
462
+ if line.is_empty() {
463
+ return self.next(); // Skip empty lines
464
+ }
465
+
466
+ match serde_json::from_str::<Value>(line) {
467
+ Ok(value) => {
468
+ if value.get("isError").is_some() {
469
+ let message = value["message"]
470
+ .as_str()
471
+ .unwrap_or("Unknown error")
472
+ .to_string();
473
+ Some(Err(Box::new(HttpException {
474
+ message,
475
+ status_code: 0,
476
+ cause: None,
477
+ })))
478
+ } else {
479
+ Some(Ok(value))
480
+ }
481
+ },
482
+ Err(e) => Some(Err(Box::new(e))),
483
+ }
484
+ },
485
+ Err(e) => Some(Err(Box::new(e))),
486
+ }
487
+ }
488
+ }
@@ -0,0 +1,81 @@
1
+ ---
2
+ imports: ['vovk-rust']
3
+ ---
4
+ <% const vars = {
5
+ convertJSONSchemasToRustTypes: t.imports['vovk-rust'].convertJSONSchemasToRustTypes,
6
+ }; %>
7
+ <%- `// auto-generated by Vovk.ts ${new Date().toISOString()}` %>
8
+ mod http_request;
9
+ mod read_full_schema;
10
+
11
+ pub use crate::http_request::HttpException;
12
+
13
+ <% Object.entries(t.schema.segments).forEach(([segmentName, segment]) => {
14
+ Object.values(segment.controllers).forEach((controllerSchema) => { %>
15
+ pub mod <%= t._.snakeCase(controllerSchema.rpcModuleName) %> {
16
+ #[allow(unused_imports)]
17
+ use crate::http_request::{HttpException, http_request, http_request_stream};
18
+ use std::collections::HashMap;
19
+ <% Object.entries(controllerSchema.handlers).forEach(([handlerNameOriginal, handlerSchema]) => {
20
+ const { validation, openapi, path, httpMethod } = handlerSchema;
21
+ const handlerName = t._.snakeCase(handlerNameOriginal);
22
+ %>
23
+ // <%= controllerSchema.rpcModuleName %>.<%= handlerName %> <%= httpMethod %> `<%= [t.apiRoot, segmentName, controllerSchema.prefix, handlerSchema.path].filter(Boolean).join('/') %>`
24
+ <%-
25
+ vars.convertJSONSchemasToRustTypes({
26
+ schemas: {
27
+ body: validation?.body,
28
+ query: validation?.query,
29
+ params: validation?.params,
30
+ output: validation?.output,
31
+ iteration: validation?.iteration
32
+ },
33
+ rootName: handlerName,
34
+ pad: 4
35
+ })
36
+ %>
37
+ <%= ([
38
+ openapi?.summary ? `Summary: ${openapi.summary}` : '',
39
+ openapi?.description ? `Description: ${openapi.description}` : '',
40
+ validation?.params?.description ? `Params: ${validation?.params?.description}` : '',
41
+ validation?.body?.description ? `Body: ${validation?.body?.description}`: '',
42
+ validation?.query?.description ? `Query: ${validation?.query?.description}`: '',
43
+ validation?.output?.description ? `Returns: ${validation?.output?.description}`: ''
44
+ ]).filter(Boolean).map((s) => s.split('\n')).flat().map((s) => ' '.repeat(4) + '/// ' + s).join('\n') %>
45
+ pub fn <%= handlerName %>(
46
+ body: <%- validation?.body ? (validation?.body?.['x-formData'] ?'reqwest::blocking::multipart::Form' : `${handlerName}_::body`): '()' %>,
47
+ query: <%- validation?.query ? `${handlerName}_::query` : '()' %>,
48
+ params: <%- validation?.params ? `${handlerName}_::params` : '()' %>,
49
+ headers: Option<&HashMap<String, String>>,
50
+ api_root: Option<&str>,
51
+ disable_client_validation: bool,
52
+ ) -> <%- validation?.output ? `Result<${handlerName}_::output, HttpException>` : validation?.iteration ? `Result<Box<dyn Iterator<Item = ${handlerName}_::iteration>>, HttpException>` : 'Result<serde_json::Value, HttpException>' %>{
53
+ let result = <%= validation?.iteration ? 'http_request_stream' : 'http_request' %>::<
54
+ <%- [
55
+ validation?.output ? `${handlerName}_::output` : validation?.iteration ? `${handlerName}_::iteration` : 'serde_json::Value',
56
+ validation?.body && !validation?.body?.['x-formData'] ? `${handlerName}_::body` : '()',
57
+ validation?.query ? `${handlerName}_::query` : '()',
58
+ validation?.params ? `${handlerName}_::params` : '()'
59
+ ].filter(Boolean).map((s) => ' '.repeat(12) + s).join(',\n') %>
60
+ >(
61
+ "<%= t.apiRoot %>",
62
+ "<%= segmentName %>",
63
+ "<%= controllerSchema.rpcModuleName %>",
64
+ "<%= handlerNameOriginal %>",
65
+ <%- !validation?.body || !validation?.body?.['x-formData'] ? `Some(&body)` : 'None' %>,
66
+ <%- validation?.body && validation?.body?.['x-formData'] ? `Some(body)` : 'None' %>,
67
+ Some(&query),
68
+ Some(&params),
69
+ headers,
70
+ api_root,
71
+ disable_client_validation,
72
+ );
73
+
74
+ result
75
+ }
76
+ <% }) %>
77
+ }
78
+ <% }) %>
79
+
80
+ <% }) %>
81
+