@arrirpc/codegen-rust 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 };