@arrirpc/codegen-rust 0.52.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs ADDED
@@ -0,0 +1,1978 @@
1
+ import { execSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import { removeDisallowedChars, snakeCase, pascalCase, isSchemaFormElements, isSchemaFormValues, isServiceDefinition, isRpcDefinition, defineGeneratorPlugin, unflattenProcedures, isSchemaFormType, isSchemaFormProperties, isSchemaFormEnum, isSchemaFormDiscriminator, isSchemaFormRef } from '@arrirpc/codegen-utils';
4
+ import path from 'pathe';
5
+ import assert from 'assert';
6
+
7
+ const reservedKeywords = [
8
+ "as",
9
+ "break",
10
+ "const",
11
+ "continue",
12
+ "crate",
13
+ "else",
14
+ "enum",
15
+ "extern",
16
+ "false",
17
+ "fn",
18
+ "for",
19
+ "if",
20
+ "impl",
21
+ "in",
22
+ "let",
23
+ "loop",
24
+ "match",
25
+ "mod",
26
+ "move",
27
+ "mut",
28
+ "pub",
29
+ "ref",
30
+ "return",
31
+ "self",
32
+ "Self",
33
+ "static",
34
+ "struct",
35
+ "super",
36
+ "trait",
37
+ "true",
38
+ "type",
39
+ "unsafe",
40
+ "use",
41
+ "where",
42
+ "while",
43
+ "async",
44
+ "await",
45
+ "dyn",
46
+ "abstract",
47
+ "become",
48
+ "box",
49
+ "do",
50
+ "final",
51
+ "macro",
52
+ "override",
53
+ "priv",
54
+ "typeof",
55
+ "unsized",
56
+ "virtual",
57
+ "yield",
58
+ "try",
59
+ "macro_rules",
60
+ "union",
61
+ "'static",
62
+ "dyn"
63
+ ];
64
+ const illegalChars = `.!@#$%^&*()-+=\\][{}'";?`;
65
+ const numberChars = "0123456789";
66
+ function validRustIdentifier(key) {
67
+ const output = removeDisallowedChars(snakeCase(key), illegalChars);
68
+ if (numberChars.includes(output.charAt(0))) {
69
+ return `r#_${output}`;
70
+ }
71
+ if (reservedKeywords.includes(output)) {
72
+ return `r#${output}`;
73
+ }
74
+ return output;
75
+ }
76
+ function validRustName(name) {
77
+ const output = removeDisallowedChars(
78
+ pascalCase(name, { normalize: true }),
79
+ illegalChars
80
+ );
81
+ if (numberChars.includes(output.charAt(0))) {
82
+ return `r#_${output}`;
83
+ }
84
+ if (reservedKeywords.includes(output)) {
85
+ return `r#${output}`;
86
+ }
87
+ return output;
88
+ }
89
+ path.resolve(__dirname, "../.temp");
90
+ function outputIsOptionType(schema, context) {
91
+ return schema.nullable === true || context.isOptional === true;
92
+ }
93
+ function getTypeName(schema, context) {
94
+ if (schema.metadata?.id) {
95
+ return validRustName(schema.metadata.id);
96
+ }
97
+ if (context.discriminatorKey && context.discriminatorValue) {
98
+ const parts = context.instancePath.split("/");
99
+ const name = validRustName(
100
+ `${parts.join("_")}_${context.discriminatorValue}`
101
+ );
102
+ return name;
103
+ }
104
+ return validRustName(context.instancePath.split("/").join("_"));
105
+ }
106
+ function formatDescriptionComment(description, leading = "") {
107
+ return description.split("\n").map((line) => `${leading}/// ${line}`).join("\n");
108
+ }
109
+
110
+ function rustAnyFromSchema(schema, context) {
111
+ return {
112
+ typeName: context.isOptional ? `Option<serde_json::Value>` : "serde_json::Value",
113
+ defaultValue: context.isOptional ? `None` : `serde_json::Value::Null`,
114
+ isNullable: false,
115
+ fromJsonTemplate(input, key) {
116
+ const innerKey = validRustIdentifier(`${key}_val`);
117
+ if (context.isOptional) {
118
+ return `match ${input} {
119
+ Some(${innerKey}) => Some(${innerKey}.to_owned()),
120
+ _ => None,
121
+ }`;
122
+ }
123
+ return `match ${input} {
124
+ Some(${innerKey}) => ${innerKey}.to_owned(),
125
+ _ => serde_json::Value::Null,
126
+ }`;
127
+ },
128
+ toJsonTemplate(input, target) {
129
+ return `${target}.push_str(
130
+ serde_json::to_string(${input})
131
+ .unwrap_or("null".to_string())
132
+ .as_str(),
133
+ )`;
134
+ },
135
+ toQueryStringTemplate() {
136
+ return `println!("[WARNING] cannot serialize any's to query params. Skipping field at ${context.instancePath}.")`;
137
+ },
138
+ content: ""
139
+ };
140
+ }
141
+
142
+ function rustArrayFromSchema(schema, context) {
143
+ const innerType = rustTypeFromSchema(schema.elements, {
144
+ instancePath: `${context.instancePath}/element`,
145
+ schemaPath: `${context.schemaPath}/elements`,
146
+ clientVersion: context.clientVersion,
147
+ clientName: context.clientName,
148
+ typeNamePrefix: context.typeNamePrefix,
149
+ generatedTypes: context.generatedTypes
150
+ });
151
+ const isOptionType = outputIsOptionType(schema, context);
152
+ const typeName = isOptionType ? `Option<Vec<${innerType.typeName}>>` : `Vec<${innerType.typeName}>`;
153
+ const defaultValue = isOptionType ? `None` : `Vec::new()`;
154
+ return {
155
+ typeName,
156
+ defaultValue,
157
+ isNullable: schema.nullable ?? false,
158
+ fromJsonTemplate(input, key) {
159
+ const innerKey = validRustIdentifier(`${key}_val`);
160
+ if (isOptionType) {
161
+ return `match ${input} {
162
+ Some(serde_json::Value::Array(${innerKey})) => {
163
+ let mut ${innerKey}_result: Vec<${innerType.typeName}> = Vec::new();
164
+ for ${innerKey}_element in ${innerKey} {
165
+ ${innerKey}_result.push(${innerType.fromJsonTemplate(`Some(${innerKey}_element)`, `${innerKey}_element`)});
166
+ }
167
+ Some(${innerKey}_result)
168
+ }
169
+ _ => None,
170
+ }`;
171
+ }
172
+ return `match ${input} {
173
+ Some(serde_json::Value::Array(${innerKey})) => {
174
+ let mut ${innerKey}_result: Vec<${innerType.typeName}> = Vec::new();
175
+ for ${innerKey}_element in ${innerKey} {
176
+ ${innerKey}_result.push(${innerType.fromJsonTemplate(`Some(${innerKey}_element)`, `${innerKey}_element`)});
177
+ }
178
+ ${innerKey}_result
179
+ }
180
+ _ => Vec::new(),
181
+ }`;
182
+ },
183
+ toJsonTemplate(input, target) {
184
+ let innerTypeToJson = innerType.toJsonTemplate(`_element_`, target);
185
+ if (innerType.isNullable) {
186
+ innerTypeToJson = `match _element_ {
187
+ Some(_element_val_) => {
188
+ ${innerType.toJsonTemplate(`_element_val_`, target)};
189
+ },
190
+ _ => {
191
+ ${target}.push_str("null");
192
+ }
193
+ }`;
194
+ }
195
+ return `${target}.push('[');
196
+ for (_index_, _element_) in ${input}.iter().enumerate() {
197
+ if _index_ != 0 {
198
+ ${target}.push(',');
199
+ }
200
+ ${innerTypeToJson};
201
+ }
202
+ ${target}.push(']')`;
203
+ },
204
+ toQueryStringTemplate() {
205
+ return `println!("[WARNING] cannot serialize arrays to query params. Skipping field at ${context.instancePath}.")`;
206
+ },
207
+ content: innerType.content
208
+ };
209
+ }
210
+
211
+ function rustTaggedUnionFromSchema(schema, context) {
212
+ const enumName = `${context.typeNamePrefix}${getTypeName(schema, context)}`;
213
+ const isOptionType = outputIsOptionType(schema, context);
214
+ const defaultValue = isOptionType ? "None" : `${enumName}::new()`;
215
+ const result = {
216
+ typeName: isOptionType ? `Option<${enumName}>` : enumName,
217
+ defaultValue,
218
+ isNullable: isOptionType,
219
+ fromJsonTemplate(input, key) {
220
+ const innerKey = validRustIdentifier(`${key}_val`);
221
+ if (isOptionType) {
222
+ return `match ${input} {
223
+ Some(${innerKey}) => match ${innerKey} {
224
+ serde_json::Value::Object(_) => {
225
+ Some(${enumName}::from_json(${innerKey}.to_owned()))
226
+ }
227
+ _ => None,
228
+ },
229
+ _ => None,
230
+ }`;
231
+ }
232
+ return `match ${input} {
233
+ Some(${innerKey}) => match ${innerKey} {
234
+ serde_json::Value::Object(_) => {
235
+ ${enumName}::from_json(${innerKey}.to_owned())
236
+ }
237
+ _ => ${enumName}::new(),
238
+ },
239
+ _ => ${enumName}::new(),
240
+ }`;
241
+ },
242
+ toJsonTemplate(input, target) {
243
+ return `${target}.push_str(${input}.to_json_string().as_str())`;
244
+ },
245
+ toQueryStringTemplate() {
246
+ return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`;
247
+ },
248
+ content: ""
249
+ };
250
+ if (context.generatedTypes.includes(enumName)) {
251
+ return result;
252
+ }
253
+ const discriminatorKey = schema.discriminator;
254
+ const discriminatorValues = Object.keys(schema.mapping);
255
+ if (discriminatorValues.length === 0)
256
+ throw new Error(
257
+ `Discriminator schemas must have at least one subtype. Issue at ${context.schemaPath}.`
258
+ );
259
+ const subTypes = [];
260
+ const subTypeContent = [];
261
+ const discriminatorKeyProperty = validRustIdentifier(discriminatorKey);
262
+ const fromJsonParts = [];
263
+ for (const discriminatorValue of discriminatorValues) {
264
+ const subTypeName = validRustName(discriminatorValue);
265
+ const subSchema = schema.mapping[discriminatorValue];
266
+ const subType = {
267
+ name: subTypeName,
268
+ properties: [],
269
+ toJsonParts: [],
270
+ toQueryParts: [],
271
+ isDeprecated: subSchema.metadata?.isDeprecated ?? false,
272
+ description: subSchema.metadata?.description ?? ""
273
+ };
274
+ fromJsonParts.push(`"${discriminatorValue}" => {`);
275
+ subType.toJsonParts.push(
276
+ ` _json_output_.push_str("\\"${discriminatorKey}\\":\\"${discriminatorValue}\\"");`
277
+ );
278
+ subType.toQueryParts.push(
279
+ `_query_parts_.push(format!("${discriminatorKey}=${discriminatorValue}"));`
280
+ );
281
+ for (const key of Object.keys(subSchema.properties)) {
282
+ const keySchema = subSchema.properties[key];
283
+ const keyType = rustTypeFromSchema(keySchema, {
284
+ clientVersion: context.clientVersion,
285
+ clientName: context.clientName,
286
+ typeNamePrefix: context.typeNamePrefix,
287
+ instancePath: `${context.instancePath}/${key}`,
288
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
289
+ generatedTypes: context.generatedTypes,
290
+ discriminatorKey,
291
+ discriminatorValue
292
+ });
293
+ if (keyType.content)
294
+ subTypeContent.push(keyType.content);
295
+ const keyName = validRustIdentifier(key);
296
+ subType.properties.push({
297
+ name: keyName,
298
+ defaultValue: keyType.defaultValue,
299
+ typeName: keyType.typeName,
300
+ isDeprecated: keySchema.metadata?.isDeprecated ?? false,
301
+ description: keySchema.metadata?.description ?? ""
302
+ });
303
+ fromJsonParts.push(
304
+ `let ${keyName} = ${keyType.fromJsonTemplate(`_val_.get("${key}")`, key)};`
305
+ );
306
+ subType.toJsonParts.push(
307
+ ` _json_output_.push_str(",\\"${key}\\":");`
308
+ );
309
+ if (keyType.isNullable) {
310
+ const innerKey = validRustIdentifier(`${key}_val`);
311
+ subType.toJsonParts.push(`match ${keyName} {
312
+ Some(${innerKey}) => {
313
+ ${keyType.toJsonTemplate(innerKey, "_json_output_")};
314
+ }
315
+ _ => {
316
+ _json_output_.push_str("null");
317
+ }
318
+ };`);
319
+ } else {
320
+ subType.toJsonParts.push(
321
+ `${keyType.toJsonTemplate(keyName, "_json_output_")};`
322
+ );
323
+ }
324
+ subType.toQueryParts.push(
325
+ `${keyType.toQueryStringTemplate(keyName, key, "_query_parts_")};`
326
+ );
327
+ }
328
+ for (const key of Object.keys(subSchema.optionalProperties ?? {})) {
329
+ const keySchema = subSchema.optionalProperties[key];
330
+ const keyType = rustTypeFromSchema(keySchema, {
331
+ clientVersion: context.clientVersion,
332
+ clientName: context.clientName,
333
+ typeNamePrefix: context.typeNamePrefix,
334
+ instancePath: `${context.instancePath}/key`,
335
+ schemaPath: `${context.schemaPath}/mapping/${key}`,
336
+ generatedTypes: context.generatedTypes,
337
+ discriminatorKey,
338
+ discriminatorValue
339
+ });
340
+ if (keyType.content)
341
+ subTypeContent.push(keyType.content);
342
+ const keyName = validRustIdentifier(key);
343
+ subType.properties.push({
344
+ name: keyName,
345
+ defaultValue: keyType.defaultValue,
346
+ typeName: keyType.typeName,
347
+ isDeprecated: keySchema.metadata?.isDeprecated ?? false,
348
+ description: keySchema.metadata?.description ?? ""
349
+ });
350
+ fromJsonParts.push(
351
+ `let ${keyName} = ${keyType.fromJsonTemplate(`_val_.get("${key}")`, key)};`
352
+ );
353
+ subType.toJsonParts.push(
354
+ ` _json_output_.push_str(",\\"${key}\\":");`
355
+ );
356
+ if (keyType.isNullable) {
357
+ const innerKey = validRustIdentifier(`${key}_val`);
358
+ subType.toJsonParts.push(`match ${keyName} {
359
+ Some(${innerKey}) => {
360
+ ${keyType.toJsonTemplate(innerKey, "_json_output_")};
361
+ }
362
+ _ => {
363
+ _json_output_.push_str("null");
364
+ }
365
+ };`);
366
+ } else {
367
+ subType.toJsonParts.push(
368
+ `${keyType.toJsonTemplate(keyName, "_json_output_")};`
369
+ );
370
+ }
371
+ }
372
+ fromJsonParts.push(`Self::${subTypeName} {
373
+ ${subType.properties.map((prop) => `${prop.name},`).join("\n")}
374
+ }`);
375
+ fromJsonParts.push(`}`);
376
+ subTypes.push(subType);
377
+ }
378
+ let leading = "";
379
+ if (schema.metadata?.description) {
380
+ leading += formatDescriptionComment(schema.metadata.description);
381
+ leading += "\n";
382
+ }
383
+ if (schema.metadata?.isDeprecated) {
384
+ leading += "#[deprecated]\n";
385
+ }
386
+ result.content = `${leading}#[derive(Clone, Debug, PartialEq)]
387
+ pub enum ${enumName} {
388
+ ${subTypes.map((type) => {
389
+ let leading2 = "";
390
+ if (type.description) {
391
+ leading2 += formatDescriptionComment(type.description);
392
+ leading2 += "\n";
393
+ }
394
+ if (type.isDeprecated) {
395
+ leading2 += "#[deprecated]\n";
396
+ }
397
+ return `${leading2}${type.name} {
398
+ ${type.properties.map((prop) => {
399
+ let leading3 = "";
400
+ if (prop.description) {
401
+ leading3 += formatDescriptionComment(prop.description);
402
+ leading3 += "\n";
403
+ }
404
+ if (prop.isDeprecated) {
405
+ leading3 += "#[deprecated]\n";
406
+ }
407
+ return `${leading3}${prop.name}: ${prop.typeName},`;
408
+ }).join("\n")}
409
+ },`;
410
+ }).join("\n")}
411
+ }
412
+
413
+ impl ArriModel for ${enumName} {
414
+ fn new() -> Self {
415
+ Self::${subTypes[0].name} {
416
+ ${subTypes[0]?.properties.map((prop) => `${prop.name}: ${prop.defaultValue},`).join("\n")}
417
+ }
418
+ }
419
+
420
+ fn from_json(input: serde_json::Value) -> Self {
421
+ match input {
422
+ serde_json::Value::Object(_val_) => {
423
+ let ${discriminatorKeyProperty} = match _val_.get("${discriminatorKey}") {
424
+ Some(serde_json::Value::String(${discriminatorKeyProperty}_val)) => ${discriminatorKeyProperty}_val.to_owned(),
425
+ _ => "".to_string(),
426
+ };
427
+ match ${discriminatorKeyProperty}.as_str() {
428
+ ${fromJsonParts.join("\n")}
429
+ _ => Self::new(),
430
+ }
431
+ }
432
+ _ => Self::new(),
433
+ }
434
+ }
435
+
436
+ fn from_json_string(input: String) -> Self {
437
+ match serde_json::from_str(input.as_str()) {
438
+ Ok(val) => Self::from_json(val),
439
+ _ => Self::new(),
440
+ }
441
+ }
442
+
443
+ fn to_json_string(&self) -> String {
444
+ let mut _json_output_ = "{".to_string();
445
+ match &self {
446
+ ${subTypes.map(
447
+ (type) => `Self::${type.name} { ${type.properties.map((prop) => `${prop.name},`).join("\n")}} => {
448
+ ${type.toJsonParts.join("\n")}
449
+ }`
450
+ )}
451
+ }
452
+ _json_output_.push('}');
453
+ _json_output_
454
+ }
455
+
456
+ fn to_query_params_string(&self) -> String {
457
+ let mut _query_parts_: Vec<String> = Vec::new();
458
+ match &self {
459
+ ${subTypes.map(
460
+ (type) => `Self::${type.name} { ${type.properties.map((prop) => `${prop.name},`).join("\n")}} => {
461
+ ${type.toQueryParts.join("\n")}
462
+ }`
463
+ )}
464
+ }
465
+ _query_parts_.join("&")
466
+ }
467
+ }
468
+
469
+ ${subTypeContent.join("\n\n")}`;
470
+ context.generatedTypes.push(enumName);
471
+ return result;
472
+ }
473
+
474
+ function rustEnumFromSchema(schema, context) {
475
+ const enumName = `${context.typeNamePrefix}${getTypeName(schema, context)}`;
476
+ const isOptionType = outputIsOptionType(schema, context);
477
+ const typeName = isOptionType ? `Option<${enumName}>` : enumName;
478
+ const defaultValue = isOptionType ? "None" : `${enumName}::default()`;
479
+ const result = {
480
+ typeName,
481
+ defaultValue,
482
+ isNullable: schema.nullable ?? false,
483
+ fromJsonTemplate(input, key) {
484
+ const innerKey = validRustIdentifier(`${key}_val`);
485
+ if (isOptionType) {
486
+ return `match ${input} {
487
+ Some(serde_json::Value::String(${innerKey})) => {
488
+ Some(${enumName}::from_string(${innerKey}.to_owned()))
489
+ }
490
+ _ => None,
491
+ }`;
492
+ }
493
+ return `match ${input} {
494
+ Some(serde_json::Value::String(${innerKey})) => {
495
+ ${enumName}::from_string(${innerKey}.to_owned())
496
+ }
497
+ _ => ${enumName}::default(),
498
+ }`;
499
+ },
500
+ toJsonTemplate(input, target) {
501
+ return `${target}.push_str(format!("\\"{}\\"", ${input}.serial_value()).as_str())`;
502
+ },
503
+ toQueryStringTemplate(input, key, target) {
504
+ const innerKey = validRustIdentifier(`${key}_val`);
505
+ if (context.isOptional) {
506
+ return `match ${input} {
507
+ Some(${innerKey}) => {
508
+ ${target}.push(format!("${key}={}", ${innerKey}.serial_value()));
509
+ }
510
+ _ => {}
511
+ }`;
512
+ }
513
+ if (schema.nullable) {
514
+ return `match ${input} {
515
+ Some(${innerKey}) => {
516
+ ${target}.push(format!("${key}={}", ${innerKey}.serial_value()));
517
+ }
518
+ _ => {
519
+ ${target}.push("${key}=null".to_string());
520
+ }
521
+ }`;
522
+ }
523
+ return `${target}.push(format!("${key}={}", ${input}.serial_value()))`;
524
+ },
525
+ content: ""
526
+ };
527
+ if (context.generatedTypes.includes(enumName)) {
528
+ return result;
529
+ }
530
+ let defaultEnumValue = "";
531
+ const initializationParts = [];
532
+ const fromStringParts = [];
533
+ const serialValueParts = [];
534
+ for (let i = 0; i < schema.enum.length; i++) {
535
+ const val = schema.enum[i];
536
+ const valName = validRustName(val);
537
+ if (i === 0) {
538
+ defaultEnumValue = valName;
539
+ }
540
+ initializationParts.push(` ${valName},`);
541
+ fromStringParts.push(` "${val}" => Self::${valName},`);
542
+ serialValueParts.push(
543
+ ` ${enumName}::${valName} => "${val}".to_string(),`
544
+ );
545
+ }
546
+ let leading = "";
547
+ if (schema.metadata?.description) {
548
+ leading += `${formatDescriptionComment(schema.metadata.description)}
549
+ `;
550
+ }
551
+ if (schema.metadata?.isDeprecated) {
552
+ leading += `#[deprecated]
553
+ `;
554
+ }
555
+ result.content = `${leading}#[derive(Clone, Debug, PartialEq)]
556
+ pub enum ${enumName} {
557
+ ${initializationParts.join("\n")}
558
+ }
559
+
560
+ impl ArriEnum for ${enumName} {
561
+ fn default() -> Self {
562
+ ${enumName}::${defaultEnumValue}
563
+ }
564
+ fn from_string(input: String) -> Self {
565
+ match input.as_str() {
566
+ ${fromStringParts.join("\n")}
567
+ _ => Self::default(),
568
+ }
569
+ }
570
+ fn serial_value(&self) -> String {
571
+ match &self {
572
+ ${serialValueParts.join("\n")}
573
+ }
574
+ }
575
+ }`;
576
+ context.generatedTypes.push(enumName);
577
+ return result;
578
+ }
579
+
580
+ function rustObjectFromSchema(schema, context) {
581
+ const isOptionType = outputIsOptionType(schema, context);
582
+ const structName = `${context.typeNamePrefix}${getTypeName(schema, context)}`;
583
+ const typeName = isOptionType ? `Option<${structName}>` : structName;
584
+ const defaultValue = isOptionType ? `None` : `${structName}::new()`;
585
+ const result = {
586
+ typeName,
587
+ defaultValue,
588
+ isNullable: schema.nullable ?? false,
589
+ fromJsonTemplate(input, key) {
590
+ const innerKey = validRustIdentifier(`${key}_val`);
591
+ if (isOptionType) {
592
+ return `match ${input} {
593
+ Some(${innerKey}) => match ${innerKey} {
594
+ serde_json::Value::Object(_) => {
595
+ Some(${structName}::from_json(${innerKey}.to_owned()))
596
+ }
597
+ _ => None,
598
+ },
599
+ _ => None,
600
+ }`;
601
+ }
602
+ return `match ${input} {
603
+ Some(${innerKey}) => ${structName}::from_json(${innerKey}.to_owned()),
604
+ _ => ${structName}::new(),
605
+ }`;
606
+ },
607
+ toJsonTemplate(input, target) {
608
+ return `${target}.push_str(${input}.to_json_string().as_str())`;
609
+ },
610
+ toQueryStringTemplate() {
611
+ return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`;
612
+ },
613
+ content: ""
614
+ };
615
+ if (context.generatedTypes.includes(structName)) {
616
+ return result;
617
+ }
618
+ const fieldNames = [];
619
+ const fieldDeclarationParts = [];
620
+ const defaultParts = [];
621
+ const fromJsonParts = [];
622
+ const toJsonParts = [];
623
+ const toQueryParamParams = [];
624
+ const subContent = [];
625
+ const requiredKeys = Object.keys(schema.properties);
626
+ const optionalKeys = Object.keys(schema.optionalProperties ?? {});
627
+ const hasKeys = requiredKeys.length > 0;
628
+ for (let i = 0; i < requiredKeys.length; i++) {
629
+ const key = requiredKeys[i];
630
+ const prop = schema.properties[key];
631
+ const innerType = rustTypeFromSchema(prop, {
632
+ clientVersion: context.clientVersion,
633
+ clientName: context.clientName,
634
+ typeNamePrefix: context.typeNamePrefix,
635
+ instancePath: `/${structName}/${key}`,
636
+ schemaPath: `${context.schemaPath}/properties/${key}`,
637
+ generatedTypes: context.generatedTypes
638
+ });
639
+ if (innerType.content) {
640
+ subContent.push(innerType.content);
641
+ }
642
+ const fieldName = validRustIdentifier(key);
643
+ fieldNames.push(fieldName);
644
+ let leading2 = "";
645
+ if (prop.metadata?.description) {
646
+ leading2 += formatDescriptionComment(prop.metadata.description);
647
+ leading2 += "\n";
648
+ }
649
+ if (prop.metadata?.isDeprecated) {
650
+ leading2 += ` #[deprecated]
651
+ `;
652
+ }
653
+ fieldDeclarationParts.push(
654
+ `${leading2} pub ${fieldName}: ${innerType.typeName}`
655
+ );
656
+ defaultParts.push(` ${fieldName}: ${innerType.defaultValue}`);
657
+ fromJsonParts.push(
658
+ ` let ${fieldName} = ${innerType.fromJsonTemplate(`_val_.get("${key}")`, key)};`
659
+ );
660
+ if (i === 0) {
661
+ toJsonParts.push(` _json_output_.push_str("\\"${key}\\":");`);
662
+ } else {
663
+ toJsonParts.push(` _json_output_.push_str(",\\"${key}\\":");`);
664
+ }
665
+ if (innerType.isNullable) {
666
+ const innerKey = validRustIdentifier(`${key}_val`);
667
+ toJsonParts.push(` match &self.${fieldName} {
668
+ Some(${innerKey}) => {
669
+ ${innerType.toJsonTemplate(innerKey, "_json_output_")};
670
+ }
671
+ _ => {
672
+ _json_output_.push_str("null");
673
+ }
674
+ };`);
675
+ } else {
676
+ const leading3 = isSchemaFormElements(prop) || isSchemaFormValues(prop) ? "" : "&";
677
+ toJsonParts.push(
678
+ ` ${innerType.toJsonTemplate(`${leading3}self.${fieldName}`, "_json_output_")};`
679
+ );
680
+ }
681
+ toQueryParamParams.push(
682
+ ` ${innerType.toQueryStringTemplate(`&self.${fieldName}`, key, "_query_parts_")};`
683
+ );
684
+ }
685
+ for (let i = 0; i < optionalKeys.length; i++) {
686
+ const key = optionalKeys[i];
687
+ const prop = schema.optionalProperties[key];
688
+ const innerType = rustTypeFromSchema(prop, {
689
+ clientVersion: context.clientVersion,
690
+ clientName: context.clientName,
691
+ typeNamePrefix: context.typeNamePrefix,
692
+ instancePath: `/${structName}/${key}`,
693
+ schemaPath: `${context.schemaPath}/optionalProperties/${key}`,
694
+ generatedTypes: context.generatedTypes,
695
+ isOptional: true
696
+ });
697
+ if (innerType.content) {
698
+ subContent.push(innerType.content);
699
+ }
700
+ const fieldName = validRustIdentifier(key);
701
+ fieldNames.push(fieldName);
702
+ let leading2 = prop.metadata?.description ? `${prop.metadata.description.split("\n").map((line) => ` /// ${line}`).join("\n")}
703
+ ` : "";
704
+ if (prop.metadata?.isDeprecated) {
705
+ leading2 += ` #[deprecated]
706
+ `;
707
+ }
708
+ fieldDeclarationParts.push(
709
+ `${leading2} pub ${fieldName}: ${innerType.typeName}`
710
+ );
711
+ defaultParts.push(` ${fieldName}: ${innerType.defaultValue}`);
712
+ fromJsonParts.push(
713
+ ` let ${fieldName} = ${innerType.fromJsonTemplate(`_val_.get("${key}")`, key)};`
714
+ );
715
+ if (hasKeys) {
716
+ const innerKey = validRustIdentifier(`${key}_val`);
717
+ toJsonParts.push(`match &self.${fieldName} {
718
+ Some(${innerKey}) => {
719
+ _json_output_.push_str(",\\"${key}\\":");
720
+ ${innerType.toJsonTemplate(innerKey, "_json_output_")}
721
+ },
722
+ _ => {}
723
+ };`);
724
+ } else {
725
+ const innerKey = validRustIdentifier(`${key}_val`);
726
+ toJsonParts.push(` match &self.${fieldName} {
727
+ Some(${innerKey}) => {
728
+ ${i !== 0 ? `if _has_keys_ {
729
+ _json_output_.push(',');
730
+ }` : ""}
731
+ _json_output_.push_str("\\"${key}\\":");
732
+ ${innerType.toJsonTemplate(innerKey, "_json_output_")};
733
+ ${i !== optionalKeys.length - 1 ? "_has_keys_ = true;" : ""}
734
+ }
735
+ _ => {}
736
+ };`);
737
+ }
738
+ toQueryParamParams.push(
739
+ ` ${innerType.toQueryStringTemplate(`&self.${fieldName}`, key, "_query_parts_")};`
740
+ );
741
+ }
742
+ context.generatedTypes.push(structName);
743
+ let selfDeclaration = `Self {
744
+ ${fieldNames.join(",\n ")},
745
+ }`;
746
+ if (fieldNames.length < 4) {
747
+ selfDeclaration = `Self { ${fieldNames.join(", ")} }`;
748
+ }
749
+ let leading = "";
750
+ if (schema.metadata?.description) {
751
+ leading += `${schema.metadata.description.split("\n").map((line) => `/// ${line}`).join("\n")}
752
+ `;
753
+ }
754
+ if (schema.metadata?.isDeprecated) {
755
+ leading += `#[deprecated]
756
+ `;
757
+ }
758
+ result.content = `${leading}#[derive(Clone, Debug, PartialEq)]
759
+ pub struct ${structName} {
760
+ ${fieldDeclarationParts.join(",\n")},
761
+ }
762
+
763
+ impl ArriModel for ${structName} {
764
+ fn new() -> Self {
765
+ Self {
766
+ ${defaultParts.join(",\n")},
767
+ }
768
+ }
769
+ fn from_json(input: serde_json::Value) -> Self {
770
+ match input {
771
+ serde_json::Value::Object(_val_) => {
772
+ ${fromJsonParts.join("\n")}
773
+ ${selfDeclaration}
774
+ }
775
+ _ => Self::new(),
776
+ }
777
+ }
778
+ fn from_json_string(input: String) -> Self {
779
+ match serde_json::from_str(input.as_str()) {
780
+ Ok(val) => Self::from_json(val),
781
+ _ => Self::new(),
782
+ }
783
+ }
784
+ fn to_json_string(&self) -> String {
785
+ let mut _json_output_ = "{".to_string();
786
+ ${!hasKeys ? `let mut _has_keys_ = false;` : ""}
787
+ ${toJsonParts.join("\n")}
788
+ _json_output_.push('}');
789
+ _json_output_
790
+ }
791
+ fn to_query_params_string(&self) -> String {
792
+ let mut _query_parts_: Vec<String> = Vec::new();
793
+ ${toQueryParamParams.join("\n")}
794
+ _query_parts_.join("&")
795
+ }
796
+ }
797
+
798
+ ${subContent.join("\n\n")}`;
799
+ return result;
800
+ }
801
+
802
+ function rustStringFromSchema(schema, context) {
803
+ const isOptionType = outputIsOptionType(schema, context);
804
+ const defaultValue = isOptionType ? "None" : '"".to_string()';
805
+ const typeName = isOptionType ? "Option<String>" : "String";
806
+ return {
807
+ typeName,
808
+ defaultValue,
809
+ isNullable: schema.nullable ?? false,
810
+ fromJsonTemplate(input, key) {
811
+ const innerKey = validRustIdentifier(`${key}_val`);
812
+ if (isOptionType) {
813
+ return `match ${input} {
814
+ Some(serde_json::Value::String(${innerKey})) => Some(${innerKey}.to_owned()),
815
+ _ => None,
816
+ }`;
817
+ }
818
+ return `match ${input} {
819
+ Some(serde_json::Value::String(${innerKey})) => ${innerKey}.to_owned(),
820
+ _ => "".to_string(),
821
+ }`;
822
+ },
823
+ toJsonTemplate(input, target) {
824
+ return `${target}.push_str(serialize_string(${input}).as_str())`;
825
+ },
826
+ toQueryStringTemplate(input, key, target) {
827
+ const innerKey = validRustIdentifier(`${key}_val`);
828
+ if (context.isOptional) {
829
+ return `match ${input} {
830
+ Some(${innerKey}) => {
831
+ ${target}.push(format!("${key}={}", ${innerKey}));
832
+ }
833
+ _ => {}
834
+ }`;
835
+ }
836
+ if (schema.nullable) {
837
+ return `match ${input} {
838
+ Some(${innerKey}) => {
839
+ ${target}.push(format!("${key}={}", ${innerKey}));
840
+ }
841
+ _ => {
842
+ ${target}.push("${key}=null".to_string());
843
+ }
844
+ }`;
845
+ }
846
+ return `${target}.push(format!("${key}={}", ${input}))`;
847
+ },
848
+ content: ""
849
+ };
850
+ }
851
+ function rustBooleanFromSchema(schema, context) {
852
+ const isOptionType = outputIsOptionType(schema, context);
853
+ const defaultValue = isOptionType ? "None" : "false";
854
+ const typeName = isOptionType ? "Option<bool>" : "bool";
855
+ return {
856
+ typeName,
857
+ defaultValue,
858
+ isNullable: schema.nullable ?? false,
859
+ fromJsonTemplate(input, key) {
860
+ const innerKey = validRustIdentifier(`${key}_val`);
861
+ if (isOptionType) {
862
+ return `match ${input} {
863
+ Some(serde_json::Value::Bool(${innerKey})) => Some(${innerKey}.to_owned()),
864
+ _ => None,
865
+ }`;
866
+ }
867
+ return `match ${input} {
868
+ Some(serde_json::Value::Bool(${innerKey})) => ${innerKey}.to_owned(),
869
+ _ => false,
870
+ }`;
871
+ },
872
+ toJsonTemplate(input, target) {
873
+ return `${target}.push_str(${input}.to_string().as_str())`;
874
+ },
875
+ toQueryStringTemplate(input, key, target) {
876
+ const innerKey = validRustIdentifier(`${key}_val`);
877
+ if (context.isOptional) {
878
+ return `match ${input} {
879
+ Some(${innerKey}) => {
880
+ ${target}.push(format!("${key}={}", ${innerKey}));
881
+ }
882
+ _ => {}
883
+ }`;
884
+ }
885
+ if (schema.nullable) {
886
+ return `match ${input} {
887
+ Some(${innerKey}) => {
888
+ ${target}.push(format!("${key}={}", ${innerKey}));
889
+ }
890
+ _ => {
891
+ ${target}.push("${key}=null".to_string());
892
+ }
893
+ }`;
894
+ }
895
+ return `${target}.push(format!("${key}={}", ${input}))`;
896
+ },
897
+ content: ""
898
+ };
899
+ }
900
+ function rustTimestampFromSchema(schema, context) {
901
+ const isOptionType = outputIsOptionType(schema, context);
902
+ const typeName = isOptionType ? "Option<DateTime<FixedOffset>>" : "DateTime<FixedOffset>";
903
+ const defaultValue = isOptionType ? "None" : "DateTime::default()";
904
+ return {
905
+ typeName,
906
+ defaultValue,
907
+ isNullable: schema.nullable ?? false,
908
+ fromJsonTemplate(input, key) {
909
+ const innerKey = validRustIdentifier(`${key}_val`);
910
+ if (isOptionType) {
911
+ return `match ${input} {
912
+ Some(serde_json::Value::String(${innerKey})) => {
913
+ match DateTime::<FixedOffset>::parse_from_rfc3339(${innerKey}) {
914
+ Ok(${innerKey}_result) => Some(${innerKey}_result),
915
+ Err(_) => None,
916
+ }
917
+ }
918
+ _ => None,
919
+ }`;
920
+ }
921
+ return `match ${input} {
922
+ Some(serde_json::Value::String(${innerKey})) => {
923
+ DateTime::<FixedOffset>::parse_from_rfc3339(${innerKey})
924
+ .unwrap_or(DateTime::default())
925
+ }
926
+ _ => DateTime::default(),
927
+ }`;
928
+ },
929
+ toJsonTemplate(input, target) {
930
+ return `${target}.push_str(serialize_date_time(${input}, true).as_str())`;
931
+ },
932
+ toQueryStringTemplate(input, key, target) {
933
+ const innerKey = validRustIdentifier(`${key}_val`);
934
+ if (context.isOptional) {
935
+ return `match ${input} {
936
+ Some(${innerKey}) => {
937
+ ${target}.push(format!(
938
+ "${key}={}",
939
+ serialize_date_time(${innerKey}, false)
940
+ ));
941
+ }
942
+ _ => {}
943
+ }`;
944
+ }
945
+ if (schema.nullable) {
946
+ return `match ${input} {
947
+ Some(${innerKey}) => {
948
+ ${target}.push(format!(
949
+ "${key}={}",
950
+ serialize_date_time(${innerKey}, false)
951
+ ));
952
+ }
953
+ _ => {
954
+ ${target}.push("${key}=null".to_string());
955
+ }
956
+ }`;
957
+ }
958
+ return `${target}.push(format!(
959
+ "${key}={}",
960
+ serialize_date_time(${input}, false)
961
+ ))`;
962
+ },
963
+ content: ""
964
+ };
965
+ }
966
+ function rustF32FromSchema(schema, context) {
967
+ const isOptionType = outputIsOptionType(schema, context);
968
+ const typeName = isOptionType ? `Option<f32>` : "f32";
969
+ const defaultValue = isOptionType ? `None` : "0.0";
970
+ return {
971
+ typeName,
972
+ defaultValue,
973
+ isNullable: schema.nullable ?? false,
974
+ fromJsonTemplate(input, key) {
975
+ const innerKey = validRustIdentifier(`${key}_val`);
976
+ if (isOptionType) {
977
+ return `match ${input} {
978
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_f64() {
979
+ Some(${innerKey}_result) => Some(${innerKey}_result as f32),
980
+ _ => None,
981
+ },
982
+ _ => None,
983
+ }`;
984
+ }
985
+ return `match ${input} {
986
+ Some(serde_json::Value::Number(${innerKey})) => {
987
+ ${innerKey}.as_f64().unwrap_or(0.0) as f32
988
+ }
989
+ _ => 0.0,
990
+ }`;
991
+ },
992
+ toJsonTemplate(input, target) {
993
+ return `${target}.push_str(${input}.to_string().as_str())`;
994
+ },
995
+ toQueryStringTemplate(input, key, target) {
996
+ const innerKey = validRustIdentifier(`${key}_val`);
997
+ if (context.isOptional) {
998
+ return `match ${input} {
999
+ Some(${innerKey}) => {
1000
+ ${target}.push(format!("${key}={}", ${innerKey}));
1001
+ }
1002
+ _ => {}
1003
+ }`;
1004
+ }
1005
+ if (schema.nullable) {
1006
+ return `match ${input} {
1007
+ Some(${innerKey}) => {
1008
+ ${target}.push(format!("${key}={}", ${innerKey}));
1009
+ }
1010
+ _ => {
1011
+ ${target}.push("${key}=null".to_string());
1012
+ }
1013
+ }`;
1014
+ }
1015
+ return `${target}.push(format!("${key}={}", ${input}))`;
1016
+ },
1017
+ content: ""
1018
+ };
1019
+ }
1020
+ function rustF64FromSchema(schema, context) {
1021
+ const isOptionType = outputIsOptionType(schema, context);
1022
+ const typeName = isOptionType ? `Option<f64>` : "f64";
1023
+ const defaultValue = isOptionType ? `None` : "0.0";
1024
+ return {
1025
+ typeName,
1026
+ defaultValue,
1027
+ isNullable: schema.nullable ?? false,
1028
+ fromJsonTemplate(input, key) {
1029
+ const innerKey = validRustIdentifier(`${key}_val`);
1030
+ if (isOptionType) {
1031
+ return `match ${input} {
1032
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_f64() {
1033
+ Some(${innerKey}_result) => Some(${innerKey}_result),
1034
+ _ => None,
1035
+ },
1036
+ _ => None,
1037
+ }`;
1038
+ }
1039
+ return `match ${input} {
1040
+ Some(serde_json::Value::Number(${innerKey})) => {
1041
+ ${innerKey}.as_f64().unwrap_or(0.0)
1042
+ }
1043
+ _ => 0.0,
1044
+ }`;
1045
+ },
1046
+ toJsonTemplate(input, target) {
1047
+ return `${target}.push_str(${input}.to_string().as_str())`;
1048
+ },
1049
+ toQueryStringTemplate(input, key, target) {
1050
+ const innerKey = validRustIdentifier(`${key}_val`);
1051
+ if (context.isOptional) {
1052
+ return `match ${input} {
1053
+ Some(${innerKey}) => {
1054
+ ${target}.push(format!("${key}={}", ${innerKey}));
1055
+ }
1056
+ _ => {}
1057
+ }`;
1058
+ }
1059
+ if (schema.nullable) {
1060
+ return `match ${input} {
1061
+ Some(${innerKey}) => {
1062
+ ${target}.push(format!("${key}={}", ${innerKey}));
1063
+ }
1064
+ _ => {
1065
+ ${target}.push("${key}=null".to_string());
1066
+ }
1067
+ }`;
1068
+ }
1069
+ return `${target}.push(format!("${key}={}", ${input}))`;
1070
+ },
1071
+ content: ""
1072
+ };
1073
+ }
1074
+ function rustI8FromSchema(schema, context) {
1075
+ const isOptionType = outputIsOptionType(schema, context);
1076
+ const typeName = isOptionType ? `Option<i8>` : "i8";
1077
+ const defaultValue = isOptionType ? `None` : "0";
1078
+ return {
1079
+ typeName,
1080
+ defaultValue,
1081
+ isNullable: schema.nullable ?? false,
1082
+ fromJsonTemplate(input, key) {
1083
+ const innerKey = validRustIdentifier(`${key}_val`);
1084
+ if (isOptionType) {
1085
+ return `match ${input} {
1086
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() {
1087
+ Some(${innerKey}_result) => match i8::try_from(${innerKey}_result) {
1088
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1089
+ Err(_) => None,
1090
+ },
1091
+ _ => None,
1092
+ },
1093
+ _ => None,
1094
+ }`;
1095
+ }
1096
+ return `match ${input} {
1097
+ Some(serde_json::Value::Number(${innerKey})) => {
1098
+ i8::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0)
1099
+ }
1100
+ _ => 0,
1101
+ }`;
1102
+ },
1103
+ toJsonTemplate(input, target) {
1104
+ return `${target}.push_str(${input}.to_string().as_str())`;
1105
+ },
1106
+ toQueryStringTemplate(input, key, target) {
1107
+ const innerKey = validRustIdentifier(`${key}_val`);
1108
+ if (context.isOptional) {
1109
+ return `match ${input} {
1110
+ Some(${innerKey}) => {
1111
+ ${target}.push(format!("${key}={}", ${innerKey}));
1112
+ }
1113
+ _ => {}
1114
+ }`;
1115
+ }
1116
+ if (schema.nullable) {
1117
+ return `match ${input} {
1118
+ Some(${innerKey}) => {
1119
+ ${target}.push(format!("${key}={}", ${innerKey}));
1120
+ }
1121
+ _ => {
1122
+ ${target}.push("${key}=null".to_string());
1123
+ }
1124
+ }`;
1125
+ }
1126
+ return `${target}.push(format!("${key}={}", ${input}))`;
1127
+ },
1128
+ content: ""
1129
+ };
1130
+ }
1131
+ function rustU8FromSchema(schema, context) {
1132
+ const isOptionType = outputIsOptionType(schema, context);
1133
+ const typeName = isOptionType ? "Option<u8>" : "u8";
1134
+ const defaultValue = isOptionType ? "None" : "0";
1135
+ return {
1136
+ typeName,
1137
+ defaultValue,
1138
+ isNullable: schema.nullable ?? false,
1139
+ fromJsonTemplate(input, key) {
1140
+ const innerKey = validRustIdentifier(`${key}_val`);
1141
+ if (isOptionType) {
1142
+ return `match ${input} {
1143
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() {
1144
+ Some(${innerKey}_result) => match u8::try_from(${innerKey}_result) {
1145
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1146
+ Err(_) => None,
1147
+ },
1148
+ _ => None,
1149
+ },
1150
+ _ => None,
1151
+ }`;
1152
+ }
1153
+ return `match ${input} {
1154
+ Some(serde_json::Value::Number(${innerKey})) => {
1155
+ u8::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0)
1156
+ }
1157
+ _ => 0,
1158
+ }`;
1159
+ },
1160
+ toJsonTemplate(input, target) {
1161
+ return `${target}.push_str(${input}.to_string().as_str())`;
1162
+ },
1163
+ toQueryStringTemplate(input, key, target) {
1164
+ const innerKey = validRustIdentifier(`${key}_val`);
1165
+ if (context.isOptional) {
1166
+ return `match ${input} {
1167
+ Some(${innerKey}) => {
1168
+ ${target}.push(format!("${key}={}", ${innerKey}));
1169
+ }
1170
+ _ => {}
1171
+ }`;
1172
+ }
1173
+ if (schema.nullable) {
1174
+ return `match ${input} {
1175
+ Some(${innerKey}) => {
1176
+ ${target}.push(format!("${key}={}", ${innerKey}));
1177
+ }
1178
+ _ => {
1179
+ ${target}.push("${key}=null".to_string());
1180
+ }
1181
+ }`;
1182
+ }
1183
+ return `${target}.push(format!("${key}={}", ${input}))`;
1184
+ },
1185
+ content: ""
1186
+ };
1187
+ }
1188
+ function rustI16FromSchema(schema, context) {
1189
+ const isOptionType = outputIsOptionType(schema, context);
1190
+ const typeName = isOptionType ? "Option<i16>" : "i16";
1191
+ const defaultValue = isOptionType ? "None" : "0";
1192
+ return {
1193
+ typeName,
1194
+ defaultValue,
1195
+ isNullable: schema.nullable ?? false,
1196
+ fromJsonTemplate(input, key) {
1197
+ const innerKey = validRustIdentifier(`${key}_val`);
1198
+ if (isOptionType) {
1199
+ return `match ${input} {
1200
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() {
1201
+ Some(${innerKey}_result) => match i16::try_from(${innerKey}_result) {
1202
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1203
+ Err(_) => None,
1204
+ },
1205
+ _ => None,
1206
+ },
1207
+ _ => None,
1208
+ }`;
1209
+ }
1210
+ return `match ${input} {
1211
+ Some(serde_json::Value::Number(${innerKey})) => {
1212
+ i16::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0)
1213
+ }
1214
+ _ => 0,
1215
+ }`;
1216
+ },
1217
+ toJsonTemplate(input, target) {
1218
+ return `${target}.push_str(${input}.to_string().as_str())`;
1219
+ },
1220
+ toQueryStringTemplate(input, key, target) {
1221
+ const innerKey = validRustIdentifier(`${key}_val`);
1222
+ if (context.isOptional) {
1223
+ return `match ${input} {
1224
+ Some(${innerKey}) => {
1225
+ ${target}.push(format!("${key}={}", ${innerKey}));
1226
+ }
1227
+ _ => {}
1228
+ }`;
1229
+ }
1230
+ if (schema.nullable) {
1231
+ return `match ${input} {
1232
+ Some(${innerKey}) => {
1233
+ ${target}.push(format!("${key}={}", ${innerKey}));
1234
+ }
1235
+ _ => {
1236
+ ${target}.push("${key}=null".to_string());
1237
+ }
1238
+ }`;
1239
+ }
1240
+ return `${target}.push(format!("${key}={}", ${input}))`;
1241
+ },
1242
+ content: ""
1243
+ };
1244
+ }
1245
+ function rustU16FromSchema(schema, context) {
1246
+ const isOptionType = outputIsOptionType(schema, context);
1247
+ const typeName = isOptionType ? "Option<u16>" : "u16";
1248
+ const defaultValue = isOptionType ? "None" : "0";
1249
+ return {
1250
+ typeName,
1251
+ defaultValue,
1252
+ isNullable: schema.nullable ?? false,
1253
+ fromJsonTemplate(input, key) {
1254
+ const innerKey = validRustIdentifier(`${key}_val`);
1255
+ if (isOptionType) {
1256
+ return `match ${input} {
1257
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() {
1258
+ Some(${innerKey}_result) => match u16::try_from(${innerKey}_result) {
1259
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1260
+ Err(_) => None,
1261
+ },
1262
+ _ => None,
1263
+ },
1264
+ _ => None,
1265
+ }`;
1266
+ }
1267
+ return `match ${input} {
1268
+ Some(serde_json::Value::Number(${innerKey})) => {
1269
+ u16::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0)
1270
+ }
1271
+ _ => 0,
1272
+ }`;
1273
+ },
1274
+ toJsonTemplate(input, target) {
1275
+ return `${target}.push_str(${input}.to_string().as_str())`;
1276
+ },
1277
+ toQueryStringTemplate(input, key, target) {
1278
+ const innerKey = validRustIdentifier(`${key}_val`);
1279
+ if (context.isOptional) {
1280
+ return `match ${input} {
1281
+ Some(${innerKey}) => {
1282
+ ${target}.push(format!("${key}={}", ${innerKey}));
1283
+ }
1284
+ _ => {}
1285
+ }`;
1286
+ }
1287
+ if (schema.nullable) {
1288
+ return `match ${input} {
1289
+ Some(${innerKey}) => {
1290
+ ${target}.push(format!("${key}={}", ${innerKey}));
1291
+ }
1292
+ _ => {
1293
+ ${target}.push("${key}=null".to_string());
1294
+ }
1295
+ }`;
1296
+ }
1297
+ return `${target}.push(format!("${key}={}", ${input}))`;
1298
+ },
1299
+ content: ""
1300
+ };
1301
+ }
1302
+ function rustI32FromSchema(schema, context) {
1303
+ const isOptionType = outputIsOptionType(schema, context);
1304
+ const typeName = isOptionType ? "Option<i32>" : "i32";
1305
+ const defaultValue = isOptionType ? "None" : "0";
1306
+ return {
1307
+ typeName,
1308
+ defaultValue,
1309
+ isNullable: schema.nullable ?? false,
1310
+ fromJsonTemplate(input, key) {
1311
+ const innerKey = validRustIdentifier(`${key}_val`);
1312
+ if (isOptionType) {
1313
+ return `match ${input} {
1314
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_i64() {
1315
+ Some(${innerKey}_result) => match i32::try_from(${innerKey}_result) {
1316
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1317
+ Err(_) => None,
1318
+ },
1319
+ _ => None,
1320
+ },
1321
+ _ => None,
1322
+ }`;
1323
+ }
1324
+ return `match ${input} {
1325
+ Some(serde_json::Value::Number(${innerKey})) => {
1326
+ i32::try_from(${innerKey}.as_i64().unwrap_or(0)).unwrap_or(0)
1327
+ }
1328
+ _ => 0,
1329
+ }`;
1330
+ },
1331
+ toJsonTemplate(input, target) {
1332
+ return `${target}.push_str(${input}.to_string().as_str())`;
1333
+ },
1334
+ toQueryStringTemplate(input, key, target) {
1335
+ const innerKey = validRustIdentifier(`${key}_val`);
1336
+ if (context.isOptional) {
1337
+ return `match ${input} {
1338
+ Some(${innerKey}) => {
1339
+ ${target}.push(format!("${key}={}", ${innerKey}));
1340
+ }
1341
+ _ => {}
1342
+ }`;
1343
+ }
1344
+ if (schema.nullable) {
1345
+ return `match ${input} {
1346
+ Some(${innerKey}) => {
1347
+ ${target}.push(format!("${key}={}", ${innerKey}));
1348
+ }
1349
+ _ => {
1350
+ ${target}.push("${key}=null".to_string());
1351
+ }
1352
+ }`;
1353
+ }
1354
+ return `${target}.push(format!("${key}={}", ${input}))`;
1355
+ },
1356
+ content: ""
1357
+ };
1358
+ }
1359
+ function rustU32FromSchema(schema, context) {
1360
+ const isOptionType = outputIsOptionType(schema, context);
1361
+ const typeName = isOptionType ? "Option<u32>" : "u32";
1362
+ const defaultValue = isOptionType ? "None" : "0";
1363
+ return {
1364
+ typeName,
1365
+ defaultValue,
1366
+ isNullable: schema.nullable ?? false,
1367
+ fromJsonTemplate(input, key) {
1368
+ const innerKey = validRustIdentifier(`${key}_val`);
1369
+ if (isOptionType) {
1370
+ return `match ${input} {
1371
+ Some(serde_json::Value::Number(${innerKey})) => match ${innerKey}.as_u64() {
1372
+ Some(${innerKey}_result) => match u32::try_from(${innerKey}_result) {
1373
+ Ok(${innerKey}_result_val) => Some(${innerKey}_result_val),
1374
+ Err(_) => None,
1375
+ },
1376
+ _ => None,
1377
+ },
1378
+ _ => None,
1379
+ }`;
1380
+ }
1381
+ return `match ${input} {
1382
+ Some(serde_json::Value::Number(${innerKey})) => {
1383
+ u32::try_from(${innerKey}.as_u64().unwrap_or(0)).unwrap_or(0)
1384
+ }
1385
+ _ => 0,
1386
+ }`;
1387
+ },
1388
+ toJsonTemplate(input, target) {
1389
+ return `${target}.push_str(${input}.to_string().as_str())`;
1390
+ },
1391
+ toQueryStringTemplate(input, key, target) {
1392
+ const innerKey = validRustIdentifier(`${key}_val`);
1393
+ if (context.isOptional) {
1394
+ return `match ${input} {
1395
+ Some(${innerKey}) => {
1396
+ ${target}.push(format!("${key}={}", ${innerKey}));
1397
+ }
1398
+ _ => {}
1399
+ }`;
1400
+ }
1401
+ if (schema.nullable) {
1402
+ return `match ${input} {
1403
+ Some(${innerKey}) => {
1404
+ ${target}.push(format!("${key}={}", ${innerKey}));
1405
+ }
1406
+ _ => {
1407
+ ${target}.push("${key}=null".to_string());
1408
+ }
1409
+ }`;
1410
+ }
1411
+ return `${target}.push(format!("${key}={}", ${input}))`;
1412
+ },
1413
+ content: ""
1414
+ };
1415
+ }
1416
+ function rustI64FromSchema(schema, context) {
1417
+ const isOptionType = outputIsOptionType(schema, context);
1418
+ const typeName = isOptionType ? "Option<i64>" : "i64";
1419
+ const defaultValue = isOptionType ? "None" : "0";
1420
+ return {
1421
+ typeName,
1422
+ defaultValue,
1423
+ isNullable: schema.nullable ?? false,
1424
+ fromJsonTemplate(input, key) {
1425
+ const innerKey = validRustIdentifier(`${key}_val`);
1426
+ if (isOptionType) {
1427
+ return `match ${input} {
1428
+ Some(serde_json::Value::String(${innerKey})) => match ${innerKey}.parse::<i64>() {
1429
+ Ok(${innerKey}_result) => Some(${innerKey}_result),
1430
+ Err(_) => None,
1431
+ },
1432
+ _ => None,
1433
+ }`;
1434
+ }
1435
+ return `match ${input} {
1436
+ Some(serde_json::Value::String(${innerKey})) => {
1437
+ ${innerKey}.parse::<i64>().unwrap_or(0)
1438
+ }
1439
+ _ => 0,
1440
+ }`;
1441
+ },
1442
+ toJsonTemplate(input, target) {
1443
+ return `${target}.push_str(format!("\\"{}\\"", ${input}).as_str())`;
1444
+ },
1445
+ toQueryStringTemplate(input, key, target) {
1446
+ const innerKey = validRustIdentifier(`${key}_val`);
1447
+ if (context.isOptional) {
1448
+ return `match ${input} {
1449
+ Some(${innerKey}) => {
1450
+ ${target}.push(format!("${key}={}", ${innerKey}));
1451
+ }
1452
+ _ => {}
1453
+ }`;
1454
+ }
1455
+ if (schema.nullable) {
1456
+ return `match ${input} {
1457
+ Some(${innerKey}) => {
1458
+ ${target}.push(format!("${key}={}", ${innerKey}));
1459
+ }
1460
+ _ => {
1461
+ ${target}.push("${key}=null".to_string());
1462
+ }
1463
+ }`;
1464
+ }
1465
+ return `${target}.push(format!("${key}={}", ${input}))`;
1466
+ },
1467
+ content: ""
1468
+ };
1469
+ }
1470
+ function rustU64FromSchema(schema, context) {
1471
+ const isOptionType = outputIsOptionType(schema, context);
1472
+ const typeName = isOptionType ? "Option<u64>" : "u64";
1473
+ const defaultValue = isOptionType ? "None" : "0";
1474
+ return {
1475
+ typeName,
1476
+ defaultValue,
1477
+ isNullable: schema.nullable ?? false,
1478
+ fromJsonTemplate(input, key) {
1479
+ const innerKey = validRustIdentifier(`${key}_val`);
1480
+ if (isOptionType) {
1481
+ return `match ${input} {
1482
+ Some(serde_json::Value::String(${innerKey})) => match ${innerKey}.parse::<u64>() {
1483
+ Ok(${innerKey}_result) => Some(${innerKey}_result),
1484
+ Err(_) => None,
1485
+ },
1486
+ _ => None,
1487
+ }`;
1488
+ }
1489
+ return `match ${input} {
1490
+ Some(serde_json::Value::String(${innerKey})) => {
1491
+ ${innerKey}.parse::<u64>().unwrap_or(0)
1492
+ }
1493
+ _ => 0,
1494
+ }`;
1495
+ },
1496
+ toJsonTemplate(input, target) {
1497
+ return `${target}.push_str(format!("\\"{}\\"", ${input}).as_str())`;
1498
+ },
1499
+ toQueryStringTemplate(input, key, target) {
1500
+ const innerKey = validRustIdentifier(`${key}_val`);
1501
+ if (context.isOptional) {
1502
+ return `match ${input} {
1503
+ Some(${innerKey}) => {
1504
+ ${target}.push(format!("${key}={}", ${innerKey}));
1505
+ }
1506
+ _ => {}
1507
+ }`;
1508
+ }
1509
+ if (schema.nullable) {
1510
+ return `match ${input} {
1511
+ Some(${innerKey}) => {
1512
+ ${target}.push(format!("${key}={}", ${innerKey}));
1513
+ }
1514
+ _ => {
1515
+ ${target}.push("${key}=null".to_string());
1516
+ }
1517
+ }`;
1518
+ }
1519
+ return `${target}.push(format!("${key}={}", ${input}))`;
1520
+ },
1521
+ content: ""
1522
+ };
1523
+ }
1524
+
1525
+ function rustRpcFromSchema(schema, context) {
1526
+ switch (schema.transport) {
1527
+ case "http":
1528
+ return rustHttpRpcFromSchema(schema, context);
1529
+ case "ws":
1530
+ return rustWsRpcFromSchema(schema, context);
1531
+ default:
1532
+ console.warn(
1533
+ `[rust-codegen] Unknown transport type "${schema.transport}". Skipping ${context.instancePath}.`
1534
+ );
1535
+ return "";
1536
+ }
1537
+ }
1538
+ function rustHttpRpcFromSchema(schema, context) {
1539
+ if (schema.isEventStream) {
1540
+ console.warn(
1541
+ `[rust-codegen] SSE is not supported at this time. Skipping ${context.instancePath}.`
1542
+ );
1543
+ return "";
1544
+ }
1545
+ const functionName = getFunctionName(context.instancePath);
1546
+ const params = schema.params ? validRustName(schema.params) : void 0;
1547
+ const response = schema.response ? validRustName(schema.response) : void 0;
1548
+ let leading = "";
1549
+ if (schema.description) {
1550
+ leading += formatDescriptionComment(schema.description);
1551
+ leading += "\n";
1552
+ }
1553
+ if (schema.isDeprecated) {
1554
+ leading += "#[deprecated]\n";
1555
+ }
1556
+ return `${leading}pub async fn ${functionName} (
1557
+ self: &Self,
1558
+ ${params ? `params: ${context.typeNamePrefix}${params},` : ""}
1559
+ ) -> Result<${context.typeNamePrefix}${response ?? "()"}, ArriServerError> {
1560
+ parsed_arri_request(
1561
+ ArriParsedRequestOptions {
1562
+ http_client: &self.config.http_client,
1563
+ url: format!("{}${schema.path}", &self.config.base_url),
1564
+ method: reqwest::Method::${schema.method.toUpperCase()},
1565
+ headers: self.config.headers,
1566
+ client_version: "${context.clientVersion}".to_string(),
1567
+ },
1568
+ ${params ? `Some(params)` : "None::<EmptyArriModel>"},
1569
+ |body| ${response ? `return ${context.typeNamePrefix}${response}::from_json_string(body)` : "{}"},
1570
+ )
1571
+ .await
1572
+ }`;
1573
+ }
1574
+ function rustWsRpcFromSchema(schema, context) {
1575
+ console.warn(
1576
+ `[rust-codegen] WS RPCs are not supported at this time. Skipping ${context.instancePath}.`
1577
+ );
1578
+ return "";
1579
+ }
1580
+ function getFunctionName(instancePath) {
1581
+ assert(instancePath.length > 0);
1582
+ const name = instancePath.split(".").pop() ?? "";
1583
+ return validRustIdentifier(name);
1584
+ }
1585
+ function getServiceName(instancePath, context) {
1586
+ assert(instancePath.length > 0);
1587
+ const name = instancePath.split(".").join("_");
1588
+ return validRustName(`${context.clientName}_${name}_Service`);
1589
+ }
1590
+ function rustServiceFromSchema(schema, context) {
1591
+ const serviceName = getServiceName(context.instancePath, context);
1592
+ const subServices = [];
1593
+ const subServiceContent = [];
1594
+ const rpcParts = [];
1595
+ for (const key of Object.keys(schema)) {
1596
+ const subSchema = schema[key];
1597
+ if (isServiceDefinition(subSchema)) {
1598
+ const subService = rustServiceFromSchema(subSchema, {
1599
+ clientVersion: context.clientVersion,
1600
+ clientName: context.clientName,
1601
+ typeNamePrefix: context.typeNamePrefix,
1602
+ instancePath: `${context.instancePath}.${key}`,
1603
+ schemaPath: `${context.schemaPath}.${key}`,
1604
+ generatedTypes: context.generatedTypes
1605
+ });
1606
+ if (subService.content) {
1607
+ subServices.push({
1608
+ key: validRustIdentifier(key),
1609
+ name: subService.name
1610
+ });
1611
+ }
1612
+ continue;
1613
+ }
1614
+ if (isRpcDefinition(subSchema)) {
1615
+ const rpc = rustRpcFromSchema(subSchema, {
1616
+ clientVersion: context.clientVersion,
1617
+ clientName: context.clientName,
1618
+ typeNamePrefix: context.typeNamePrefix,
1619
+ instancePath: `${context.instancePath}.${key}`,
1620
+ schemaPath: `${context.schemaPath}.${key}`,
1621
+ generatedTypes: context.generatedTypes
1622
+ });
1623
+ if (rpc) {
1624
+ rpcParts.push(rpc);
1625
+ }
1626
+ continue;
1627
+ }
1628
+ throw new Error(
1629
+ `[rust-codegen] Invalid schema at /procedures/${context.instancePath}.`
1630
+ );
1631
+ }
1632
+ return {
1633
+ name: serviceName,
1634
+ content: `pub struct ${serviceName}<'a> {
1635
+ config: &'a ArriClientConfig,
1636
+ ${subServices.map((service) => ` pub ${service.key}: ${service.name}<'a>,`).join("\n")}
1637
+ }
1638
+
1639
+ impl<'a> ArriClientService<'a> for ${serviceName}<'a> {
1640
+ fn create(config: &'a ArriClientConfig) -> Self {
1641
+ Self {
1642
+ config: &config,
1643
+ ${subServices.map((service) => ` ${service.key}: ${service.name}::create(config),`).join("\n")}
1644
+ }
1645
+ }
1646
+ }
1647
+
1648
+ impl ${serviceName}<'_> {
1649
+ ${rpcParts.join("\n")}
1650
+ }
1651
+
1652
+ ${subServiceContent.join("\n\n")}
1653
+ `
1654
+ };
1655
+ }
1656
+
1657
+ function rustRecordFromSchema(schema, context) {
1658
+ const innerType = rustTypeFromSchema(schema.values, {
1659
+ clientVersion: context.clientVersion,
1660
+ clientName: context.clientName,
1661
+ typeNamePrefix: context.typeNamePrefix,
1662
+ instancePath: `${context.instancePath}/value`,
1663
+ schemaPath: `${context.schemaPath}/values`,
1664
+ generatedTypes: context.generatedTypes
1665
+ });
1666
+ const isOptionType = outputIsOptionType(schema, context);
1667
+ const typeName = isOptionType ? `Option<BTreeMap<String, ${innerType.typeName}>>` : `BTreeMap<String, ${innerType.typeName}>`;
1668
+ const defaultValue = isOptionType ? `None` : `BTreeMap::new()`;
1669
+ return {
1670
+ typeName,
1671
+ defaultValue,
1672
+ isNullable: schema.nullable ?? false,
1673
+ fromJsonTemplate(input, key) {
1674
+ const innerKey = validRustIdentifier(`${key}_val`);
1675
+ if (isOptionType) {
1676
+ return `match ${input} {
1677
+ Some(serde_json::Value::Object(${innerKey})) => {
1678
+ let mut ${innerKey}_result: BTreeMap<String, ${innerType.typeName}> = BTreeMap::new();
1679
+ for (_key_, _value_) in ${innerKey}.into_iter() {
1680
+ ${innerKey}_result.insert(
1681
+ _key_.to_owned(),
1682
+ ${innerType.fromJsonTemplate(`Some(_value_.to_owned())`, `value`)},
1683
+ );
1684
+ }
1685
+ Some(${innerKey}_result)
1686
+ }
1687
+ _ => None,
1688
+ }`;
1689
+ }
1690
+ return `match ${input} {
1691
+ Some(serde_json::Value::Object(${innerKey})) => {
1692
+ let mut ${innerKey}_result: BTreeMap<String, ${innerType.typeName}> = BTreeMap::new();
1693
+ for (_key_, _value_) in ${innerKey}.into_iter() {
1694
+ ${innerKey}_result.insert(
1695
+ _key_.to_owned(),
1696
+ ${innerType.fromJsonTemplate(`Some(_value_.to_owned())`, `value`)},
1697
+ );
1698
+ }
1699
+ ${innerKey}_result
1700
+ }
1701
+ _ => BTreeMap::new(),
1702
+ }`;
1703
+ },
1704
+ toJsonTemplate(input, target) {
1705
+ if (innerType.isNullable) {
1706
+ return `${target}.push('{');
1707
+ for (_index_, (_key_, _value_)) in ${input}.iter().enumerate() {
1708
+ if _index_ != 0 {
1709
+ ${target}.push(',');
1710
+ }
1711
+ ${target}.push_str(format!("\\"{}\\":", _key_).as_str());
1712
+ match _value_ {
1713
+ Some(value_val) => {
1714
+ ${innerType.toJsonTemplate("value_val", target)};
1715
+ },
1716
+ _ => {
1717
+ ${target}.push_str("null");
1718
+ }
1719
+ }
1720
+ }
1721
+ ${target}.push('}')`;
1722
+ }
1723
+ return `${target}.push('{');
1724
+ for (_index_, (_key_, _value_)) in ${input}.iter().enumerate() {
1725
+ if _index_ != 0 {
1726
+ ${target}.push(',');
1727
+ }
1728
+ ${target}.push_str(format!("\\"{}\\":", _key_).as_str());
1729
+ ${innerType.toJsonTemplate(`_value_`, target)};
1730
+ }
1731
+ ${target}.push('}')`;
1732
+ },
1733
+ toQueryStringTemplate() {
1734
+ return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`;
1735
+ },
1736
+ content: innerType.content
1737
+ };
1738
+ }
1739
+
1740
+ function rustRefFromSchema(schema, context) {
1741
+ const innerTypeName = `${context.typeNamePrefix}${validRustName(schema.ref)}`;
1742
+ const isOptionType = outputIsOptionType(schema, context);
1743
+ let typeName = `Box<${innerTypeName}>`;
1744
+ if (isOptionType) {
1745
+ typeName = `Option<${typeName}>`;
1746
+ }
1747
+ let defaultValue;
1748
+ if (isOptionType) {
1749
+ defaultValue = "None";
1750
+ } else {
1751
+ defaultValue = `Box::new(${innerTypeName}::new())`;
1752
+ }
1753
+ return {
1754
+ typeName,
1755
+ defaultValue,
1756
+ isNullable: schema.nullable ?? false,
1757
+ fromJsonTemplate(input, key) {
1758
+ const innerKey = validRustIdentifier(`${key}_val`);
1759
+ const valFromJson = (input2) => {
1760
+ {
1761
+ return `Box::new(${innerTypeName}::from_json(${input2}.to_owned()))`;
1762
+ }
1763
+ };
1764
+ if (isOptionType) {
1765
+ return `match ${input} {
1766
+ Some(${innerKey}) => match ${innerKey} {
1767
+ serde_json::Value::Object(_) => {
1768
+ Some(${valFromJson(innerKey)})
1769
+ }
1770
+ _ => None,
1771
+ },
1772
+ _ => None,
1773
+ }`;
1774
+ }
1775
+ return `match ${input} {
1776
+ Some(${innerKey}) => match ${innerKey} {
1777
+ serde_json::Value::Object(_) => {
1778
+ ${valFromJson(innerKey)}
1779
+ }
1780
+ _ => ${valFromJson(innerKey)},
1781
+ },
1782
+ _ => Box::new(${innerTypeName}::new()),
1783
+ }`;
1784
+ },
1785
+ toJsonTemplate(input, target) {
1786
+ return `${target}.push_str(${input}.to_json_string().as_str())`;
1787
+ },
1788
+ toQueryStringTemplate() {
1789
+ return `println!("[WARNING] cannot serialize nested objects to query params. Skipping field at ${context.instancePath}.")`;
1790
+ },
1791
+ content: ""
1792
+ };
1793
+ }
1794
+
1795
+ const rustClientGenerator = defineGeneratorPlugin(
1796
+ (options) => {
1797
+ return {
1798
+ generator(def) {
1799
+ const context = {
1800
+ clientVersion: def.info?.version ?? "",
1801
+ clientName: options.clientName ?? "Client",
1802
+ typeNamePrefix: options.typePrefix ?? "",
1803
+ instancePath: "",
1804
+ schemaPath: "",
1805
+ generatedTypes: []
1806
+ };
1807
+ const client = createRustClient(def, {
1808
+ ...context
1809
+ });
1810
+ const outputFile = path.resolve(options.outputFile);
1811
+ fs.writeFileSync(outputFile, client);
1812
+ const shouldFormat = options.format ?? true;
1813
+ if (shouldFormat) {
1814
+ try {
1815
+ execSync(`rustfmt ${outputFile} --edition 2021`, {
1816
+ stdio: "inherit"
1817
+ });
1818
+ } catch (err) {
1819
+ console.error(`Error formatting`, err);
1820
+ }
1821
+ }
1822
+ },
1823
+ options
1824
+ };
1825
+ }
1826
+ );
1827
+ function createRustClient(def, context) {
1828
+ const services = unflattenProcedures(def.procedures);
1829
+ const rpcParts = [];
1830
+ const subServices = [];
1831
+ const subServiceContent = [];
1832
+ for (const key of Object.keys(services)) {
1833
+ const subDef = services[key];
1834
+ if (isServiceDefinition(subDef)) {
1835
+ const service = rustServiceFromSchema(subDef, {
1836
+ clientVersion: def.info?.version ?? "",
1837
+ clientName: context.clientName,
1838
+ typeNamePrefix: context.typeNamePrefix,
1839
+ instancePath: key,
1840
+ schemaPath: key,
1841
+ generatedTypes: context.generatedTypes
1842
+ });
1843
+ if (service.content) {
1844
+ subServices.push({
1845
+ key: validRustIdentifier(key),
1846
+ name: service.name
1847
+ });
1848
+ subServiceContent.push(service.content);
1849
+ }
1850
+ continue;
1851
+ }
1852
+ if (isRpcDefinition(subDef)) {
1853
+ const rpc = rustRpcFromSchema(subDef, {
1854
+ clientVersion: def.info?.version ?? "",
1855
+ clientName: context.clientName,
1856
+ typeNamePrefix: context.typeNamePrefix,
1857
+ instancePath: key,
1858
+ schemaPath: key,
1859
+ generatedTypes: context.generatedTypes
1860
+ });
1861
+ if (rpc) {
1862
+ rpcParts.push(rpc);
1863
+ }
1864
+ continue;
1865
+ }
1866
+ }
1867
+ const modelParts = [];
1868
+ for (const key of Object.keys(def.definitions)) {
1869
+ const result = rustTypeFromSchema(def.definitions[key], {
1870
+ ...context,
1871
+ clientVersion: def.info?.version ?? "",
1872
+ instancePath: key,
1873
+ schemaPath: ""
1874
+ });
1875
+ if (result.content) {
1876
+ modelParts.push(result.content);
1877
+ }
1878
+ }
1879
+ if (rpcParts.length === 0 && subServiceContent.length === 0) {
1880
+ return `#![allow(dead_code, unused_imports, unused_variables, unconditional_recursion, deprecated)]
1881
+ use arri_client::{
1882
+ chrono::{DateTime, FixedOffset},
1883
+ serde_json::{self},
1884
+ utils::{serialize_date_time, serialize_string},
1885
+ ArriEnum, ArriModel,
1886
+ };
1887
+ use std::collections::BTreeMap;
1888
+ ${modelParts.join("\n\n")}`;
1889
+ }
1890
+ const clientName = validRustName(context.clientName);
1891
+ return `#![allow(dead_code, unused_imports, unused_variables, unconditional_recursion, deprecated)]
1892
+ use arri_client::{
1893
+ chrono::{DateTime, FixedOffset},
1894
+ parsed_arri_request, reqwest, serde_json,
1895
+ utils::{serialize_date_time, serialize_string},
1896
+ ArriClientConfig, ArriClientService, ArriEnum, ArriModel, ArriParsedRequestOptions,
1897
+ ArriServerError, EmptyArriModel,
1898
+ };
1899
+ use std::collections::BTreeMap;
1900
+
1901
+ pub struct ${clientName}<'a> {
1902
+ config: &'a ArriClientConfig,
1903
+ ${subServices.map((service) => ` pub ${service.key}: ${service.name}<'a>,`).join("\n")}
1904
+ }
1905
+
1906
+ impl<'a> ArriClientService<'a> for ${clientName}<'a> {
1907
+ fn create(config: &'a ArriClientConfig) -> Self {
1908
+ Self {
1909
+ config: &config,
1910
+ ${subServices.map((service) => ` ${service.key}: ${service.name}::create(config),`).join("\n")}
1911
+ }
1912
+ }
1913
+ }
1914
+
1915
+ impl ${clientName}<'_> {
1916
+ ${rpcParts.join("\n")}
1917
+ }
1918
+
1919
+ ${subServiceContent.join("\n\n")}
1920
+
1921
+ ${modelParts.join("\n\n")}`;
1922
+ }
1923
+ function rustTypeFromSchema(schema, context) {
1924
+ if (isSchemaFormType(schema)) {
1925
+ switch (schema.type) {
1926
+ case "string":
1927
+ return rustStringFromSchema(schema, context);
1928
+ case "boolean":
1929
+ return rustBooleanFromSchema(schema, context);
1930
+ case "timestamp":
1931
+ return rustTimestampFromSchema(schema, context);
1932
+ case "float32":
1933
+ return rustF32FromSchema(schema, context);
1934
+ case "float64":
1935
+ return rustF64FromSchema(schema, context);
1936
+ case "int8":
1937
+ return rustI8FromSchema(schema, context);
1938
+ case "uint8":
1939
+ return rustU8FromSchema(schema, context);
1940
+ case "int16":
1941
+ return rustI16FromSchema(schema, context);
1942
+ case "uint16":
1943
+ return rustU16FromSchema(schema, context);
1944
+ case "int32":
1945
+ return rustI32FromSchema(schema, context);
1946
+ case "uint32":
1947
+ return rustU32FromSchema(schema, context);
1948
+ case "int64":
1949
+ return rustI64FromSchema(schema, context);
1950
+ case "uint64":
1951
+ return rustU64FromSchema(schema, context);
1952
+ default:
1953
+ schema.type;
1954
+ throw new Error(`Unhandled schema type: "${schema.type}"`);
1955
+ }
1956
+ }
1957
+ if (isSchemaFormProperties(schema)) {
1958
+ return rustObjectFromSchema(schema, context);
1959
+ }
1960
+ if (isSchemaFormEnum(schema)) {
1961
+ return rustEnumFromSchema(schema, context);
1962
+ }
1963
+ if (isSchemaFormElements(schema)) {
1964
+ return rustArrayFromSchema(schema, context);
1965
+ }
1966
+ if (isSchemaFormValues(schema)) {
1967
+ return rustRecordFromSchema(schema, context);
1968
+ }
1969
+ if (isSchemaFormDiscriminator(schema)) {
1970
+ return rustTaggedUnionFromSchema(schema, context);
1971
+ }
1972
+ if (isSchemaFormRef(schema)) {
1973
+ return rustRefFromSchema(schema, context);
1974
+ }
1975
+ return rustAnyFromSchema(schema, context);
1976
+ }
1977
+
1978
+ export { createRustClient, rustClientGenerator, rustTypeFromSchema };