@ioka-technologies/asyncapi-rust-client-template 0.0.21 → 0.0.23
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/common/index.js +3478 -0
- package/package.json +12 -3
- package/template/Cargo.toml.js +13 -38
- package/template/helpers/index.js +56 -441
- package/template/helpers/index.js.dev +116 -0
- package/template/src/client.rs.js +3 -15
- package/template/src/lib.rs.js +10 -14
- package/template/src/models.rs.js +21 -536
- package/template/package.json +0 -48
|
@@ -5,552 +5,37 @@ import {
|
|
|
5
5
|
toRustTypeName,
|
|
6
6
|
toRustFieldName,
|
|
7
7
|
toRustEnumVariant,
|
|
8
|
-
toRustEnumVariantWithSerde
|
|
9
|
-
|
|
8
|
+
toRustEnumVariantWithSerde,
|
|
9
|
+
generateRustModels,
|
|
10
|
+
generateMessageEnvelope
|
|
11
|
+
} from '../../../common/src/index.js';
|
|
10
12
|
|
|
11
13
|
export default function ModelsRs({ asyncapi }) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const nestedSchemas = new Map();
|
|
20
|
-
const schemaRegistry = new Map();
|
|
21
|
-
|
|
22
|
-
// First, build channel to message mapping and extract inline message schemas
|
|
23
|
-
if (asyncapi.channels) {
|
|
24
|
-
const channels = asyncapi.channels();
|
|
25
|
-
if (channels) {
|
|
26
|
-
// Use proper iteration for AsyncAPI collection
|
|
27
|
-
for (const channel of channels) {
|
|
28
|
-
try {
|
|
29
|
-
const channelName = channel.id();
|
|
30
|
-
|
|
31
|
-
// Handle AsyncAPI 3.x format - extract inline messages from channels
|
|
32
|
-
if (channel.messages) {
|
|
33
|
-
const messages = channel.messages();
|
|
34
|
-
if (messages) {
|
|
35
|
-
// Check if messages is an object with message names as keys
|
|
36
|
-
if (typeof messages === 'object' && !Array.isArray(messages)) {
|
|
37
|
-
// Iterate through message entries (messageName -> messageObject)
|
|
38
|
-
Object.entries(messages).forEach(([messageName, message]) => {
|
|
39
|
-
if (message && messageName) {
|
|
40
|
-
let payload = null;
|
|
41
|
-
let description = null;
|
|
42
|
-
|
|
43
|
-
// Get payload schema
|
|
44
|
-
if (message.payload && typeof message.payload === 'function') {
|
|
45
|
-
payload = message.payload();
|
|
46
|
-
if (payload && payload.json && typeof payload.json === 'function') {
|
|
47
|
-
payload = payload.json();
|
|
48
|
-
}
|
|
49
|
-
} else if (message.payload) {
|
|
50
|
-
payload = message.payload;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Get description
|
|
54
|
-
if (message.description && typeof message.description === 'function') {
|
|
55
|
-
description = message.description();
|
|
56
|
-
} else if (message.description) {
|
|
57
|
-
description = message.description;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Add to channel mapping
|
|
61
|
-
if (!messageToChannels.has(messageName)) {
|
|
62
|
-
messageToChannels.set(messageName, []);
|
|
63
|
-
}
|
|
64
|
-
messageToChannels.get(messageName).push(channelName);
|
|
65
|
-
|
|
66
|
-
// Add to message schemas for inline messages
|
|
67
|
-
messageSchemas.push({
|
|
68
|
-
name: messageName,
|
|
69
|
-
rustName: toRustTypeName(messageName),
|
|
70
|
-
payload,
|
|
71
|
-
rawPayload: payload,
|
|
72
|
-
description,
|
|
73
|
-
channels: [channelName]
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
} else {
|
|
78
|
-
// Try iterating as a collection
|
|
79
|
-
for (const message of messages) {
|
|
80
|
-
if (message) {
|
|
81
|
-
let messageName = null;
|
|
82
|
-
let payload = null;
|
|
83
|
-
let description = null;
|
|
84
|
-
|
|
85
|
-
// Get message name - try multiple approaches
|
|
86
|
-
if (message._meta && message._meta.id) {
|
|
87
|
-
messageName = message._meta.id;
|
|
88
|
-
} else if (message._json && message._json['x-parser-message-name']) {
|
|
89
|
-
messageName = message._json['x-parser-message-name'];
|
|
90
|
-
} else if (message._json && message._json['x-parser-unique-object-id']) {
|
|
91
|
-
messageName = message._json['x-parser-unique-object-id'];
|
|
92
|
-
} else if (message.name && typeof message.name === 'function') {
|
|
93
|
-
messageName = message.name();
|
|
94
|
-
} else if (message.name) {
|
|
95
|
-
messageName = message.name;
|
|
96
|
-
} else if (message.$ref) {
|
|
97
|
-
messageName = message.$ref.split('/').pop();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Get payload schema
|
|
101
|
-
if (message.payload && typeof message.payload === 'function') {
|
|
102
|
-
payload = message.payload();
|
|
103
|
-
if (payload && payload.json && typeof payload.json === 'function') {
|
|
104
|
-
payload = payload.json();
|
|
105
|
-
}
|
|
106
|
-
} else if (message.payload) {
|
|
107
|
-
payload = message.payload;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Get description
|
|
111
|
-
if (message.description && typeof message.description === 'function') {
|
|
112
|
-
description = message.description();
|
|
113
|
-
} else if (message.description) {
|
|
114
|
-
description = message.description;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (messageName) {
|
|
118
|
-
// Add to channel mapping
|
|
119
|
-
if (!messageToChannels.has(messageName)) {
|
|
120
|
-
messageToChannels.set(messageName, []);
|
|
121
|
-
}
|
|
122
|
-
messageToChannels.get(messageName).push(channelName);
|
|
123
|
-
|
|
124
|
-
// Add to message schemas for inline messages
|
|
125
|
-
messageSchemas.push({
|
|
126
|
-
name: messageName,
|
|
127
|
-
rustName: toRustTypeName(messageName),
|
|
128
|
-
payload,
|
|
129
|
-
rawPayload: payload,
|
|
130
|
-
description,
|
|
131
|
-
channels: [channelName]
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {
|
|
140
|
-
// Ignore channel processing errors
|
|
141
|
-
console.warn(`Error processing channel: ${e.message}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Build schema registry from components.schemas
|
|
148
|
-
// Try to access the raw AsyncAPI document
|
|
149
|
-
let rawDoc = null;
|
|
150
|
-
try {
|
|
151
|
-
if (asyncapi.json && typeof asyncapi.json === 'function') {
|
|
152
|
-
rawDoc = asyncapi.json();
|
|
153
|
-
} else if (asyncapi._json) {
|
|
154
|
-
rawDoc = asyncapi._json;
|
|
155
|
-
}
|
|
156
|
-
} catch (e) {
|
|
157
|
-
// Ignore
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Extract schemas from raw document if available
|
|
161
|
-
if (rawDoc && rawDoc.components && rawDoc.components.schemas) {
|
|
162
|
-
Object.entries(rawDoc.components.schemas).forEach(([name, schema]) => {
|
|
163
|
-
if (name && typeof name === 'string' && schema && typeof schema === 'object') {
|
|
164
|
-
schemaRegistry.set(name, schema);
|
|
165
|
-
componentSchemas.push({
|
|
166
|
-
name,
|
|
167
|
-
rustName: toRustTypeName(name),
|
|
168
|
-
schema: schema,
|
|
169
|
-
description: schema.description
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Fallback: try the components.schemas() method
|
|
176
|
-
if (componentSchemas.length === 0 && components && components.schemas) {
|
|
177
|
-
try {
|
|
178
|
-
const schemas = components.schemas();
|
|
179
|
-
if (schemas) {
|
|
180
|
-
// Try different ways to access schemas
|
|
181
|
-
let schemaEntries = [];
|
|
182
|
-
|
|
183
|
-
if (schemas instanceof Map) {
|
|
184
|
-
schemaEntries = Array.from(schemas.entries());
|
|
185
|
-
} else if (typeof schemas === 'object') {
|
|
186
|
-
schemaEntries = Object.entries(schemas);
|
|
187
|
-
} else if (schemas.all && typeof schemas.all === 'function') {
|
|
188
|
-
// AsyncAPI parser might have an all() method
|
|
189
|
-
const allSchemas = schemas.all();
|
|
190
|
-
if (Array.isArray(allSchemas)) {
|
|
191
|
-
schemaEntries = allSchemas.map(schema => {
|
|
192
|
-
const name = schema.uid ? schema.uid() : (schema.id ? schema.id() : null);
|
|
193
|
-
return [name, schema];
|
|
194
|
-
}).filter(([name]) => name);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
schemaEntries.forEach(([name, schema]) => {
|
|
199
|
-
// Skip internal AsyncAPI parser objects and numeric keys
|
|
200
|
-
if (!name || name === 'collections' || name === '_meta' || name.startsWith('_') || /^\d+$/.test(name)) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
let schemaData = null;
|
|
205
|
-
let description = null;
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
// Handle different schema object types
|
|
209
|
-
if (schema && typeof schema.json === 'function') {
|
|
210
|
-
schemaData = schema.json();
|
|
211
|
-
} else if (schema && typeof schema === 'object') {
|
|
212
|
-
schemaData = schema;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (schema && typeof schema.description === 'function') {
|
|
216
|
-
description = schema.description();
|
|
217
|
-
} else if (schema && schema.description) {
|
|
218
|
-
description = schema.description;
|
|
219
|
-
}
|
|
220
|
-
} catch (e) {
|
|
221
|
-
// Ignore schema extraction errors
|
|
222
|
-
console.warn(`Failed to extract schema for ${name}:`, e.message);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (schemaData && typeof name === 'string' && name.length > 0) {
|
|
226
|
-
schemaRegistry.set(name, schemaData);
|
|
227
|
-
componentSchemas.push({
|
|
228
|
-
name,
|
|
229
|
-
rustName: toRustTypeName(name),
|
|
230
|
-
schema: schemaData,
|
|
231
|
-
description
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
} catch (e) {
|
|
237
|
-
console.warn('Failed to extract component schemas:', e.message);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Extract messages from components
|
|
242
|
-
if (components && components.messages) {
|
|
243
|
-
const messages = components.messages();
|
|
244
|
-
if (messages) {
|
|
245
|
-
Object.entries(messages).forEach(([name, message]) => {
|
|
246
|
-
let payload = null;
|
|
247
|
-
let description = null;
|
|
248
|
-
let title = null;
|
|
249
|
-
let messageName = name;
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
let rawPayload = null;
|
|
253
|
-
if (message.payload && typeof message.payload === 'function') {
|
|
254
|
-
const payloadSchema = message.payload();
|
|
255
|
-
payload = payloadSchema && payloadSchema.json ? payloadSchema.json() : payloadSchema;
|
|
256
|
-
// Try to get the raw payload reference from the message
|
|
257
|
-
if (message._json && message._json.payload) {
|
|
258
|
-
rawPayload = message._json.payload;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
description = message.description && typeof message.description === 'function' ? message.description() : null;
|
|
262
|
-
title = message.title && typeof message.title === 'function' ? message.title() : null;
|
|
263
|
-
|
|
264
|
-
// Try to get the actual message name
|
|
265
|
-
if (message.name && typeof message.name === 'function') {
|
|
266
|
-
messageName = message.name();
|
|
267
|
-
} else if (message.name) {
|
|
268
|
-
messageName = message.name;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// If we have raw document access, try to get the payload reference from there
|
|
272
|
-
if (!rawPayload && rawDoc && rawDoc.components && rawDoc.components.messages && rawDoc.components.messages[name]) {
|
|
273
|
-
rawPayload = rawDoc.components.messages[name].payload;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
|
|
277
|
-
messageSchemas.push({
|
|
278
|
-
name: messageName,
|
|
279
|
-
rustName: toRustTypeName(messageName),
|
|
280
|
-
payload,
|
|
281
|
-
rawPayload,
|
|
282
|
-
description: description || title,
|
|
283
|
-
channels
|
|
284
|
-
});
|
|
285
|
-
} catch (e) {
|
|
286
|
-
// Ignore payload extraction errors
|
|
287
|
-
const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
|
|
288
|
-
messageSchemas.push({
|
|
289
|
-
name: messageName,
|
|
290
|
-
rustName: toRustTypeName(messageName),
|
|
291
|
-
payload,
|
|
292
|
-
rawPayload: null,
|
|
293
|
-
description: description || title,
|
|
294
|
-
channels
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Helper function to convert JSON schema to Rust type
|
|
302
|
-
function jsonSchemaToRustType(schema, typeName = null) {
|
|
303
|
-
if (!schema) return 'serde_json::Value';
|
|
304
|
-
|
|
305
|
-
// Handle $ref
|
|
306
|
-
if (schema.$ref) {
|
|
307
|
-
const refName = schema.$ref.split('/').pop();
|
|
308
|
-
const rustTypeName = toRustTypeName(refName);
|
|
309
|
-
|
|
310
|
-
// Generate the referenced schema if we have access to components
|
|
311
|
-
if (components && components.schemas) {
|
|
312
|
-
const schemas = components.schemas();
|
|
313
|
-
if (schemas && schemas[refName] && !generatedTypes.has(rustTypeName)) {
|
|
314
|
-
generatedTypes.add(rustTypeName);
|
|
315
|
-
const referencedSchema = schemas[refName];
|
|
316
|
-
const schemaJson = referencedSchema.json ? referencedSchema.json() : referencedSchema;
|
|
317
|
-
nestedSchemas.set(rustTypeName, {
|
|
318
|
-
type: 'struct',
|
|
319
|
-
schema: schemaJson,
|
|
320
|
-
description: referencedSchema.description && typeof referencedSchema.description === 'function' ? referencedSchema.description() : null
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return rustTypeName;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Handle resolved $ref - check for x-parser-schema-id which indicates original schema name
|
|
329
|
-
if (schema['x-parser-schema-id'] && typeof schema['x-parser-schema-id'] === 'string') {
|
|
330
|
-
const schemaId = schema['x-parser-schema-id'];
|
|
331
|
-
// Check if this matches a known component schema
|
|
332
|
-
if (schemaRegistry.has(schemaId)) {
|
|
333
|
-
const rustTypeName = toRustTypeName(schemaId);
|
|
334
|
-
return rustTypeName;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (!schema.type) {
|
|
339
|
-
// If no type specified, check for properties (object) or items (array)
|
|
340
|
-
if (schema.properties) {
|
|
341
|
-
schema.type = 'object';
|
|
342
|
-
} else if (schema.items) {
|
|
343
|
-
schema.type = 'array';
|
|
344
|
-
} else {
|
|
345
|
-
return 'serde_json::Value';
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
switch (schema.type) {
|
|
350
|
-
case 'string':
|
|
351
|
-
if (schema.enum && schema.enum.length > 0) {
|
|
352
|
-
// Generate enum type
|
|
353
|
-
if (typeName) {
|
|
354
|
-
const enumName = `${typeName}Enum`;
|
|
355
|
-
if (!generatedTypes.has(enumName)) {
|
|
356
|
-
generatedTypes.add(enumName);
|
|
357
|
-
nestedSchemas.set(enumName, {
|
|
358
|
-
type: 'enum',
|
|
359
|
-
variants: schema.enum,
|
|
360
|
-
description: schema.description
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
return enumName;
|
|
364
|
-
}
|
|
365
|
-
return 'String'; // Fallback if no type name provided
|
|
366
|
-
}
|
|
367
|
-
if (schema.format === 'date-time') return 'chrono::DateTime<chrono::Utc>';
|
|
368
|
-
if (schema.format === 'uuid') return 'uuid::Uuid';
|
|
369
|
-
if (schema.format === 'email') return 'String';
|
|
370
|
-
if (schema.format === 'uri') return 'String';
|
|
371
|
-
return 'String';
|
|
372
|
-
case 'integer':
|
|
373
|
-
switch (schema.format) {
|
|
374
|
-
case 'int32':
|
|
375
|
-
return 'i32';
|
|
376
|
-
case 'int64':
|
|
377
|
-
return 'i64';
|
|
378
|
-
case 'uint32':
|
|
379
|
-
return 'u32';
|
|
380
|
-
case 'uint64':
|
|
381
|
-
return 'u64';
|
|
382
|
-
default:
|
|
383
|
-
// Default to i32 for unspecified format (maintains backward compatibility)
|
|
384
|
-
return 'i32';
|
|
385
|
-
}
|
|
386
|
-
case 'number':
|
|
387
|
-
return 'f64';
|
|
388
|
-
case 'boolean':
|
|
389
|
-
return 'bool';
|
|
390
|
-
case 'array': {
|
|
391
|
-
const itemType = jsonSchemaToRustType(schema.items);
|
|
392
|
-
return `Vec<${itemType}>`;
|
|
393
|
-
}
|
|
394
|
-
case 'object':
|
|
395
|
-
if (schema.properties && Object.keys(schema.properties).length > 0) {
|
|
396
|
-
// Generate nested struct
|
|
397
|
-
if (typeName) {
|
|
398
|
-
const structName = toRustTypeName(typeName);
|
|
399
|
-
if (!generatedTypes.has(structName)) {
|
|
400
|
-
generatedTypes.add(structName);
|
|
401
|
-
nestedSchemas.set(structName, {
|
|
402
|
-
type: 'struct',
|
|
403
|
-
schema: schema,
|
|
404
|
-
description: schema.description
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
return structName;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
return 'serde_json::Value';
|
|
411
|
-
default:
|
|
412
|
-
return 'serde_json::Value';
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Generate message structs
|
|
417
|
-
function generateMessageStruct(schema, messageName) {
|
|
418
|
-
if (!schema || !schema.properties) {
|
|
419
|
-
return ' pub data: serde_json::Value,';
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const fields = Object.entries(schema.properties).map(([fieldName, fieldSchema]) => {
|
|
423
|
-
const rustFieldName = toRustFieldName(fieldName);
|
|
424
|
-
const fieldTypeName = `${messageName}${toRustTypeName(fieldName)}`;
|
|
425
|
-
const rustType = jsonSchemaToRustType(fieldSchema, fieldTypeName);
|
|
426
|
-
const requiredFields = schema.required;
|
|
427
|
-
const optional = !requiredFields || !Array.isArray(requiredFields) || requiredFields.indexOf(fieldName) === -1;
|
|
428
|
-
const finalType = optional ? `Option<${rustType}>` : rustType;
|
|
429
|
-
|
|
430
|
-
let fieldDoc = '';
|
|
431
|
-
if (fieldSchema.description) {
|
|
432
|
-
fieldDoc = ` /// ${fieldSchema.description}\n`;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
let serdeRename = '';
|
|
436
|
-
if (rustFieldName !== fieldName) {
|
|
437
|
-
serdeRename = ` #[serde(rename = "${fieldName}")]\n`;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
let skipSerializing = '';
|
|
441
|
-
if (optional) {
|
|
442
|
-
skipSerializing = ' #[serde(skip_serializing_if = "Option::is_none")]\n';
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return `${fieldDoc}${serdeRename}${skipSerializing} pub ${rustFieldName}: ${finalType},`;
|
|
446
|
-
}).join('\n');
|
|
447
|
-
|
|
448
|
-
return fields;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Process all component schemas first to ensure they are available for references
|
|
452
|
-
componentSchemas.forEach(schema => {
|
|
453
|
-
jsonSchemaToRustType(schema.schema, schema.rustName);
|
|
454
|
-
generatedTypes.add(schema.rustName);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// Process all message schemas to ensure all referenced types are generated
|
|
458
|
-
messageSchemas.forEach(schema => {
|
|
459
|
-
if (schema.payload) {
|
|
460
|
-
jsonSchemaToRustType(schema.payload, schema.rustName);
|
|
461
|
-
}
|
|
14
|
+
// Generate models using the common helper
|
|
15
|
+
const models = generateRustModels(asyncapi, {
|
|
16
|
+
toRustTypeName,
|
|
17
|
+
toRustFieldName,
|
|
18
|
+
toRustEnumVariantWithSerde,
|
|
19
|
+
includeAsyncApiTrait: false, // Client doesn't need the AsyncApiMessage trait
|
|
20
|
+
includeEnvelope: false // We'll generate the envelope separately
|
|
462
21
|
});
|
|
463
22
|
|
|
464
|
-
// Generate
|
|
465
|
-
|
|
466
|
-
let result = '';
|
|
467
|
-
|
|
468
|
-
componentSchemas.forEach(schema => {
|
|
469
|
-
const doc = schema.description ? `/// ${schema.description}\n` : `/// ${schema.name}\n`;
|
|
470
|
-
|
|
471
|
-
// Check if this is a standalone enum schema
|
|
472
|
-
if (schema.schema.type === 'string' && schema.schema.enum && Array.isArray(schema.schema.enum)) {
|
|
473
|
-
// Generate enum definition with serde rename attributes for lowercase serialization
|
|
474
|
-
const variants = schema.schema.enum.map(variant => {
|
|
475
|
-
const { rustName, serializedName } = toRustEnumVariantWithSerde(variant);
|
|
476
|
-
return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
|
|
477
|
-
}).join(',\n');
|
|
478
|
-
result += `
|
|
479
|
-
${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
480
|
-
pub enum ${schema.rustName} {
|
|
481
|
-
${variants},
|
|
482
|
-
}
|
|
483
|
-
`;
|
|
484
|
-
} else {
|
|
485
|
-
// Generate struct definition
|
|
486
|
-
const fields = generateMessageStruct(schema.schema, schema.rustName);
|
|
487
|
-
result += `
|
|
488
|
-
${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
489
|
-
pub struct ${schema.rustName} {
|
|
490
|
-
${fields}
|
|
491
|
-
}
|
|
492
|
-
`;
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
return result;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Generate nested type definitions
|
|
500
|
-
function generateNestedTypes() {
|
|
501
|
-
let result = '';
|
|
502
|
-
|
|
503
|
-
for (const [typeName, typeInfo] of nestedSchemas.entries()) {
|
|
504
|
-
// Don't skip enums - they need to be generated even if the parent type exists
|
|
505
|
-
const isEnum = typeInfo.type === 'enum';
|
|
506
|
-
const isComponentSchema = componentSchemas.some(cs => cs.rustName === typeName);
|
|
507
|
-
|
|
508
|
-
// Skip if this type was already generated as a component schema (but not enums)
|
|
509
|
-
if (!isEnum && isComponentSchema) {
|
|
510
|
-
continue;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (typeInfo.type === 'enum') {
|
|
514
|
-
const variants = typeInfo.variants.map(variant => {
|
|
515
|
-
const { rustName, serializedName } = toRustEnumVariantWithSerde(variant);
|
|
516
|
-
return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
|
|
517
|
-
}).join(',\n');
|
|
518
|
-
const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
|
|
519
|
-
result += `
|
|
520
|
-
${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
521
|
-
pub enum ${typeName} {
|
|
522
|
-
${variants},
|
|
523
|
-
}
|
|
524
|
-
`;
|
|
525
|
-
} else if (typeInfo.type === 'struct') {
|
|
526
|
-
const fields = generateMessageStruct(typeInfo.schema, typeName);
|
|
527
|
-
const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
|
|
528
|
-
result += `
|
|
529
|
-
${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
530
|
-
pub struct ${typeName} {
|
|
531
|
-
${fields}
|
|
532
|
-
}
|
|
533
|
-
`;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
return result;
|
|
538
|
-
}
|
|
23
|
+
// Generate the unified message envelope
|
|
24
|
+
const envelopeCode = generateMessageEnvelope();
|
|
539
25
|
|
|
540
26
|
return (
|
|
541
27
|
<File name="models.rs">
|
|
542
28
|
{`//! Generated data models from AsyncAPI specification
|
|
543
29
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
${
|
|
547
|
-
${generateNestedTypes()}
|
|
30
|
+
${envelopeCode}
|
|
31
|
+
${models.generateComponentSchemas()}
|
|
32
|
+
${models.generateNestedTypes()}
|
|
548
33
|
${(() => {
|
|
549
34
|
// Track which types have already had implementations generated
|
|
550
35
|
const implementedTypes = new Set();
|
|
551
36
|
const implementations = [];
|
|
552
37
|
|
|
553
|
-
messageSchemas.forEach(schema => {
|
|
38
|
+
models.messageSchemas.forEach(schema => {
|
|
554
39
|
const doc = schema.description ? `/// ${schema.description}` : `/// ${schema.name} message`;
|
|
555
40
|
|
|
556
41
|
// Check if the message payload references a component schema
|
|
@@ -568,7 +53,7 @@ ${(() => {
|
|
|
568
53
|
} else if (schema.payload && schema.payload['x-parser-schema-id']) {
|
|
569
54
|
// Handle resolved $ref references
|
|
570
55
|
const schemaId = schema.payload['x-parser-schema-id'];
|
|
571
|
-
if (schemaRegistry.has(schemaId)) {
|
|
56
|
+
if (models.schemaRegistry.has(schemaId)) {
|
|
572
57
|
payloadRustName = toRustTypeName(schemaId);
|
|
573
58
|
isComponentMessage = true;
|
|
574
59
|
}
|
|
@@ -585,14 +70,14 @@ pub struct ${schema.rustName} {
|
|
|
585
70
|
#[serde(flatten)]
|
|
586
71
|
pub payload: ${payloadRustName},
|
|
587
72
|
}`);
|
|
588
|
-
} else if (!generatedTypes.has(schema.rustName) && !implementedTypes.has(schema.rustName)) {
|
|
73
|
+
} else if (!models.generatedTypes.has(schema.rustName) && !implementedTypes.has(schema.rustName)) {
|
|
589
74
|
// Generate both struct for inline message schemas
|
|
590
75
|
implementedTypes.add(schema.rustName);
|
|
591
76
|
implementations.push(`
|
|
592
77
|
${doc}
|
|
593
78
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
594
79
|
pub struct ${schema.rustName} {
|
|
595
|
-
${generateMessageStruct(schema.payload, schema.rustName)}
|
|
80
|
+
${models.generateMessageStruct(schema.payload, schema.rustName)}
|
|
596
81
|
}`);
|
|
597
82
|
}
|
|
598
83
|
});
|
|
@@ -600,7 +85,7 @@ ${generateMessageStruct(schema.payload, schema.rustName)}
|
|
|
600
85
|
return implementations.join('');
|
|
601
86
|
})()}
|
|
602
87
|
|
|
603
|
-
${messageSchemas.length === 0 && componentSchemas.length === 0 ? `
|
|
88
|
+
${models.messageSchemas.length === 0 && models.componentSchemas.length === 0 ? `
|
|
604
89
|
/// Example message structure when no messages are defined in the spec
|
|
605
90
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
606
91
|
pub struct ExampleMessage {
|
package/template/package.json
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@asyncapi/rust-client-template",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"description": "AsyncAPI template for generating Rust NATS clients",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"asyncapi",
|
|
8
|
-
"template",
|
|
9
|
-
"rust",
|
|
10
|
-
"nats",
|
|
11
|
-
"client",
|
|
12
|
-
"generator"
|
|
13
|
-
],
|
|
14
|
-
"author": "AsyncAPI Generator",
|
|
15
|
-
"license": "Apache-2.0",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@asyncapi/generator-react-sdk": "^1.0.0"
|
|
18
|
-
},
|
|
19
|
-
"generator": {
|
|
20
|
-
"renderer": "react",
|
|
21
|
-
"apiVersion": "v3",
|
|
22
|
-
"supportedProtocols": [
|
|
23
|
-
"nats"
|
|
24
|
-
],
|
|
25
|
-
"parameters": {
|
|
26
|
-
"clientName": {
|
|
27
|
-
"description": "Name of the generated client struct",
|
|
28
|
-
"default": "{{asyncapi.info().title() | replace(/[^a-zA-Z0-9]/g, '') }}Client"
|
|
29
|
-
},
|
|
30
|
-
"packageName": {
|
|
31
|
-
"description": "Name of the generated Rust crate",
|
|
32
|
-
"default": "{{asyncapi.info().title() | kebabCase}}-client"
|
|
33
|
-
},
|
|
34
|
-
"packageVersion": {
|
|
35
|
-
"description": "Version of the generated crate",
|
|
36
|
-
"default": "{{asyncapi.info().version()}}"
|
|
37
|
-
},
|
|
38
|
-
"author": {
|
|
39
|
-
"description": "Author of the generated crate",
|
|
40
|
-
"default": "AsyncAPI Generator"
|
|
41
|
-
},
|
|
42
|
-
"license": {
|
|
43
|
-
"description": "License of the generated crate",
|
|
44
|
-
"default": "Apache-2.0"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|