vovk-rust 0.0.1-beta.14 → 0.0.1-beta.16

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.
@@ -46,6 +46,8 @@ fn prepare_request<B, Q, P>(
46
46
  handler_name: &str,
47
47
  body: Option<&B>,
48
48
  form: Option<multipart::Form>,
49
+ text_body: Option<String>,
50
+ binary_body: Option<(Vec<u8>, String)>,
49
51
  query: Option<&Q>,
50
52
  params: Option<&P>,
51
53
  headers: Option<&HashMap<String, String>>,
@@ -111,8 +113,8 @@ where
111
113
  .transpose()
112
114
  .map_err(|e| format!("Failed to serialize params: {}", e))?;
113
115
 
114
- // Perform JSON validation if not disabled and no form data is provided
115
- if !disable_client_validation && form.is_none() {
116
+ // Perform JSON validation if not disabled and no form/text/binary data is provided
117
+ if !disable_client_validation && form.is_none() && text_body.is_none() && binary_body.is_none() {
116
118
  if let Some(body_schema) = validation.get("body") {
117
119
  if let Some(ref body_val) = body_value {
118
120
  let schema =
@@ -189,8 +191,12 @@ where
189
191
  // Set up request headers
190
192
  let mut headers_map = reqwest::header::HeaderMap::new();
191
193
  headers_map.insert("Accept", "application/jsonl, application/json".parse().unwrap());
192
- if body_value.is_some() && form.is_none() {
194
+ if body_value.is_some() && form.is_none() && text_body.is_none() && binary_body.is_none() {
193
195
  headers_map.insert("Content-Type", "application/json".parse().unwrap());
196
+ } else if text_body.is_some() {
197
+ headers_map.insert("Content-Type", "text/plain".parse().unwrap());
198
+ } else if let Some((_, ref content_type)) = binary_body {
199
+ headers_map.insert("Content-Type", content_type.parse().unwrap());
194
200
  }
195
201
 
196
202
  // Merge with user-provided headers if any
@@ -220,9 +226,13 @@ where
220
226
  let client = Client::new();
221
227
  let mut request = client.request(method, &url).headers(headers_map);
222
228
 
223
- // Apply form data or JSON body to the request
229
+ // Apply form data, text body, binary body, or JSON body to the request
224
230
  if let Some(form_data) = form {
225
231
  request = request.multipart(form_data);
232
+ } else if let Some(text) = text_body {
233
+ request = request.body(text);
234
+ } else if let Some((bytes, _)) = binary_body {
235
+ request = request.body(bytes);
226
236
  } else if let Some(body_val) = body_value {
227
237
  request = request.json(&body_val);
228
238
  }
@@ -239,6 +249,8 @@ pub async fn http_request<T, B, Q, P>(
239
249
  handler_name: &str,
240
250
  body: Option<&B>,
241
251
  form: Option<multipart::Form>,
252
+ text_body: Option<String>,
253
+ binary_body: Option<(Vec<u8>, String)>,
242
254
  query: Option<&Q>,
243
255
  params: Option<&P>,
244
256
  headers: Option<&HashMap<String, String>>,
@@ -258,6 +270,8 @@ where
258
270
  handler_name,
259
271
  body,
260
272
  form,
273
+ text_body,
274
+ binary_body,
261
275
  query,
262
276
  params,
263
277
  headers,
@@ -344,6 +358,8 @@ pub async fn http_request_stream<T, B, Q, P>(
344
358
  handler_name: &str,
345
359
  body: Option<&B>,
346
360
  form: Option<multipart::Form>,
361
+ text_body: Option<String>,
362
+ binary_body: Option<(Vec<u8>, String)>,
347
363
  query: Option<&Q>,
348
364
  params: Option<&P>,
349
365
  headers: Option<&HashMap<String, String>>,
@@ -363,6 +379,8 @@ where
363
379
  handler_name,
364
380
  body,
365
381
  form,
382
+ text_body,
383
+ binary_body,
366
384
  query,
367
385
  params,
368
386
  headers,
@@ -45,8 +45,20 @@ vars.convertJSONSchemasToRustTypes({
45
45
  validation?.query?.description ? `Query: ${validation?.query?.description}`: '',
46
46
  validation?.output?.description ? `Returns: ${validation?.output?.description}`: ''
47
47
  ]).filter(Boolean).map((s) => s.split('\n')).flat().map((s) => ' '.repeat(4) + '/// ' + s).join('\n') %>
48
+ <%
49
+ // Determine body kind: 'none', 'form', 'binary', 'text', or 'json'
50
+ const bodyKind = (() => {
51
+ if (!validation?.body) return 'none';
52
+ const ct = validation.body['x-contentType'];
53
+ if (ct?.includes('multipart/form-data') || ct?.includes('application/x-www-form-urlencoded')) return 'form';
54
+ if (validation.body.format === 'binary' || validation.body.contentEncoding === 'binary') return 'binary';
55
+ if (ct?.some(c => c.startsWith('text/'))) return 'text';
56
+ return 'json';
57
+ })();
58
+ const firstContentType = validation?.body?.['x-contentType']?.[0] || 'application/octet-stream';
59
+ %>
48
60
  pub async fn <%= handlerName %>(
49
- body: <%- validation?.body ? (validation?.body?.['x-isForm'] ?'reqwest::multipart::Form' : `${handlerName}_::body`): '()' %>,
61
+ body: <%- bodyKind === 'form' ? 'reqwest::multipart::Form' : bodyKind !== 'none' ? `${handlerName}_::body` : '()' %>,
50
62
  query: <%- validation?.query ? `${handlerName}_::query` : '()' %>,
51
63
  params: <%- validation?.params ? `${handlerName}_::params` : '()' %>,
52
64
  headers: Option<&HashMap<String, String>>,
@@ -56,7 +68,7 @@ vars.convertJSONSchemasToRustTypes({
56
68
  let result = <%= validation?.iteration ? 'http_request_stream' : 'http_request' %>::<
57
69
  <%- [
58
70
  validation?.output ? `${handlerName}_::output` : validation?.iteration ? `${handlerName}_::iteration` : 'serde_json::Value',
59
- validation?.body && !validation?.body?.['x-isForm'] ? `${handlerName}_::body` : '()',
71
+ bodyKind === 'json' ? `${handlerName}_::body` : '()',
60
72
  validation?.query ? `${handlerName}_::query` : '()',
61
73
  validation?.params ? `${handlerName}_::params` : '()'
62
74
  ].filter(Boolean).map((s) => ' '.repeat(12) + s).join(',\n') %>
@@ -65,8 +77,10 @@ vars.convertJSONSchemasToRustTypes({
65
77
  "<%= segmentName %>",
66
78
  "<%= controllerSchema.rpcModuleName %>",
67
79
  "<%= handlerNameOriginal %>",
68
- <%- !validation?.body || !validation?.body?.['x-isForm'] ? `Some(&body)` : 'None' %>,
69
- <%- validation?.body && validation?.body?.['x-isForm'] ? `Some(body)` : 'None' %>,
80
+ <%- bodyKind === 'json' ? 'Some(&body)' : 'None' %>,
81
+ <%- bodyKind === 'form' ? 'Some(body)' : 'None' %>,
82
+ <%- bodyKind === 'text' ? 'Some(body)' : 'None' %>,
83
+ <%- bodyKind === 'binary' ? `Some((body, "${firstContentType}".to_string()))` : 'None' %>,
70
84
  Some(&query),
71
85
  Some(&params),
72
86
  headers,
package/index.js CHANGED
@@ -1,3 +1,58 @@
1
+ // Rust reserved keywords that cannot be used as identifiers
2
+ const RUST_KEYWORDS = new Set([
3
+ 'as',
4
+ 'break',
5
+ 'const',
6
+ 'continue',
7
+ 'crate',
8
+ 'else',
9
+ 'enum',
10
+ 'extern',
11
+ 'false',
12
+ 'fn',
13
+ 'for',
14
+ 'if',
15
+ 'impl',
16
+ 'in',
17
+ 'let',
18
+ 'loop',
19
+ 'match',
20
+ 'mod',
21
+ 'move',
22
+ 'mut',
23
+ 'pub',
24
+ 'ref',
25
+ 'return',
26
+ 'self',
27
+ 'Self',
28
+ 'static',
29
+ 'struct',
30
+ 'super',
31
+ 'trait',
32
+ 'true',
33
+ 'type',
34
+ 'unsafe',
35
+ 'use',
36
+ 'where',
37
+ 'while',
38
+ 'async',
39
+ 'await',
40
+ 'dyn',
41
+ 'abstract',
42
+ 'become',
43
+ 'box',
44
+ 'do',
45
+ 'final',
46
+ 'macro',
47
+ 'override',
48
+ 'priv',
49
+ 'typeof',
50
+ 'unsized',
51
+ 'virtual',
52
+ 'yield',
53
+ 'try',
54
+ 'union',
55
+ ]);
1
56
  // Helper function for indentation
2
57
  export function indent(level, pad = 0) {
3
58
  return ' '.repeat(pad + level * 2);
@@ -90,6 +145,10 @@ export function toRustType(schema, path, rootSchema = schema) {
90
145
  }
91
146
  }
92
147
  if (schema.type === 'string') {
148
+ // Binary format maps to Vec<u8>
149
+ if (schema.format === 'binary' || schema.contentEncoding === 'binary') {
150
+ return 'Vec<u8>';
151
+ }
93
152
  return 'String';
94
153
  }
95
154
  else if (schema.type === 'number' || schema.type === 'integer') {
@@ -153,7 +212,7 @@ export function toRustType(schema, path, rootSchema = schema) {
153
212
  return '()';
154
213
  }
155
214
  else if (schema.type === 'array') {
156
- if (schema.items) {
215
+ if (schema.items && typeof schema.items !== 'boolean') {
157
216
  // Check if array items are objects that need special handling
158
217
  if (schema.items.type === 'object' || schema.items.properties || schema.items.$ref) {
159
218
  // For array of objects, reference the item type with proper module path
@@ -225,8 +284,8 @@ export function generateVariantEnum(schema, name, path, level, rootSchema, pad =
225
284
  // If it's an object type, we need to create a separate struct
226
285
  if (variant.type === 'object' || variant.properties) {
227
286
  code += `${indentFn(level + 1)}${variantName}(${name}_::${variantName}),\n`;
228
- // Create a nested type definition to be added outside the enum
229
- nestedTypes += processObject(variant, variantPath, level, rootSchema, pad);
287
+ // Create a nested type definition to be added inside a sub-module
288
+ nestedTypes += processObject(variant, variantPath, level + 1, rootSchema, pad);
230
289
  }
231
290
  else {
232
291
  // For simple types, we can include them directly in the enum
@@ -235,9 +294,13 @@ export function generateVariantEnum(schema, name, path, level, rootSchema, pad =
235
294
  }
236
295
  });
237
296
  code += `${indentFn(level)}}\n\n`;
238
- // Add nested type definitions if needed
297
+ // Add nested type definitions wrapped in a sub-module
239
298
  if (nestedTypes) {
299
+ code += `${indentFn(level)}#[allow(non_snake_case)]\n`;
300
+ code += `${indentFn(level)}pub mod ${name}_ {\n`;
301
+ code += `${indentFn(level + 1)}use serde::{Serialize, Deserialize};\n\n`;
240
302
  code += nestedTypes;
303
+ code += `${indentFn(level)}}\n`;
241
304
  }
242
305
  return code;
243
306
  }
@@ -288,7 +351,9 @@ export function processObject(schema, path, level, rootSchema = schema, pad = 0)
288
351
  let code = '';
289
352
  // Add documentation comments for the struct
290
353
  code += generateDocComment(schema, level, pad);
291
- if (schema.type === 'object' && schema['x-isForm'] === true) {
354
+ if (schema.type === 'object' &&
355
+ (schema['x-contentType']?.includes('multipart/form-data') ||
356
+ schema['x-contentType']?.includes('application/x-www-form-urlencoded'))) {
292
357
  code += `${indentFn(level)}pub use reqwest::multipart::Form as ${currentName};\n`;
293
358
  return code;
294
359
  }
@@ -337,7 +402,13 @@ export function processObject(schema, path, level, rootSchema = schema, pad = 0)
337
402
  if (!isRequired) {
338
403
  propType = `Option<${propType}>`;
339
404
  }
340
- code += `${indentFn(level + 1)}pub ${propName}: ${propType},\n`;
405
+ if (RUST_KEYWORDS.has(propName)) {
406
+ code += `${indentFn(level + 1)}#[serde(rename = "${propName}")]\n`;
407
+ code += `${indentFn(level + 1)}pub r#${propName}: ${propType},\n`;
408
+ }
409
+ else {
410
+ code += `${indentFn(level + 1)}pub ${propName}: ${propType},\n`;
411
+ }
341
412
  });
342
413
  code += `${indentFn(level)}}\n\n`;
343
414
  // Check if any properties require nested types before generating the sub-module
@@ -352,6 +423,7 @@ export function processObject(schema, path, level, rootSchema = schema, pad = 0)
352
423
  ((propSchema.type === 'string' || !propSchema.type) && propSchema.enum) ||
353
424
  (propSchema.type === 'array' &&
354
425
  propSchema.items &&
426
+ typeof propSchema.items !== 'boolean' &&
355
427
  (propSchema.items.type === 'object' || propSchema.items.properties || propSchema.items.$ref)) ||
356
428
  propSchema.anyOf ||
357
429
  propSchema.oneOf ||
@@ -379,7 +451,7 @@ export function processObject(schema, path, level, rootSchema = schema, pad = 0)
379
451
  code += generateEnum(propSchema, propName, level + 1, pad);
380
452
  }
381
453
  // Generate types for array items if they're objects
382
- else if (propSchema.type === 'array' && propSchema.items) {
454
+ else if (propSchema.type === 'array' && propSchema.items && typeof propSchema.items !== 'boolean') {
383
455
  // Check if items has a $ref
384
456
  if (propSchema.items.$ref) {
385
457
  const resolved = resolveRef(propSchema.items.$ref, rootSchema);
@@ -409,7 +481,13 @@ export function processPrimitive(schema, name, level, pad = 0) {
409
481
  // For primitive types, create a type alias
410
482
  code += `${indentFn(level)}pub type ${name} = `;
411
483
  if (schema.type === 'string') {
412
- code += 'String';
484
+ // Binary format maps to Vec<u8>
485
+ if (schema.format === 'binary' || schema.contentEncoding === 'binary') {
486
+ code += 'Vec<u8>';
487
+ }
488
+ else {
489
+ code += 'String';
490
+ }
413
491
  }
414
492
  else if (schema.type === 'number') {
415
493
  code += 'f64';
@@ -441,7 +519,9 @@ export function convertJSONSchemasToRustTypes({ schemas, pad = 0, rootName, }) {
441
519
  }
442
520
  const indentFn = (level) => ' '.repeat(pad + level * 2);
443
521
  // Start code generation
444
- let result = `${indentFn(0)}pub mod ${rootName}_ {\n`;
522
+ let result = `${indentFn(0)}#[allow(non_camel_case_types)]\n`;
523
+ result += `${indentFn(0)}pub mod ${rootName}_ {\n`;
524
+ result += `${indentFn(1)}#[allow(unused_imports)]\n`;
445
525
  result += `${indentFn(1)}use serde::{Serialize, Deserialize};\n`;
446
526
  // Process each schema in the schemas object
447
527
  Object.entries(schemas).forEach(([schemaName, schemaObj]) => {
@@ -460,7 +540,7 @@ export function convertJSONSchemasToRustTypes({ schemas, pad = 0, rootName, }) {
460
540
  required: defSchema.required || [],
461
541
  title: defSchema.title,
462
542
  description: defSchema.description,
463
- 'x-isForm': defSchema['x-isForm'],
543
+ 'x-contentType': defSchema['x-contentType'],
464
544
  };
465
545
  result += processObject(rootDefObject, [defName], 1, schemaObj, pad);
466
546
  }
@@ -470,7 +550,8 @@ export function convertJSONSchemasToRustTypes({ schemas, pad = 0, rootName, }) {
470
550
  else if (defSchema.anyOf || defSchema.oneOf || defSchema.allOf) {
471
551
  result += generateVariantEnum(defSchema, defName, [defName], 1, schemaObj, pad);
472
552
  }
473
- else if (defSchema.type && ['string', 'number', 'integer', 'boolean', 'null'].includes(defSchema.type)) {
553
+ else if (typeof defSchema.type === 'string' &&
554
+ ['string', 'number', 'integer', 'boolean', 'null'].includes(defSchema.type)) {
474
555
  // Handle primitive types in $defs
475
556
  result += processPrimitive(defSchema, defName, 1, pad);
476
557
  }
@@ -486,11 +567,12 @@ export function convertJSONSchemasToRustTypes({ schemas, pad = 0, rootName, }) {
486
567
  required: schemaObj.required || [],
487
568
  title: schemaObj.title,
488
569
  description: schemaObj.description,
489
- 'x-isForm': schemaObj['x-isForm'],
570
+ 'x-contentType': schemaObj['x-contentType'],
490
571
  };
491
572
  result += processObject(rootObject, [schemaName], 1, schemaObj, pad);
492
573
  }
493
- else if (['string', 'number', 'integer', 'boolean', 'null'].includes(schemaObj.type)) {
574
+ else if (typeof schemaObj.type === 'string' &&
575
+ ['string', 'number', 'integer', 'boolean', 'null'].includes(schemaObj.type)) {
494
576
  // Handle primitive schema
495
577
  result += processPrimitive(schemaObj, schemaName, 1, pad);
496
578
  }
@@ -505,7 +587,7 @@ export function convertJSONSchemasToRustTypes({ schemas, pad = 0, rootName, }) {
505
587
  else if (schemaObj.type === 'array') {
506
588
  // For array as root type, create a type alias to Vec<ItemType>
507
589
  let itemType = 'String'; // Default if no items specified
508
- if (schemaObj.items) {
590
+ if (schemaObj.items && typeof schemaObj.items !== 'boolean') {
509
591
  if (schemaObj.items.type === 'object' || schemaObj.items.properties) {
510
592
  // Create the item type
511
593
  const itemSchema = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-rust",
3
- "version": "0.0.1-beta.14",
3
+ "version": "0.0.1-beta.16",
4
4
  "description": "Codegen templates for Rust client library for Vovk.ts",
5
5
  "files": [
6
6
  "client-templates",