@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ioka-technologies/asyncapi-rust-client-template",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "AsyncAPI template for generating Rust NATS clients",
|
|
5
5
|
"main": "template/index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -14,12 +14,21 @@
|
|
|
14
14
|
"author": "AsyncAPI Generator",
|
|
15
15
|
"license": "Apache-2.0",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@asyncapi/generator-react-sdk": "^1.0.
|
|
17
|
+
"@asyncapi/generator-react-sdk": "^1.0.20"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
+
"build": "webpack --config ../webpack.config.js --env template=rust-client",
|
|
21
|
+
"build:dev": "webpack --config ../webpack.config.js --env template=rust-client --mode development",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
20
23
|
"test": "npm run test:nats",
|
|
21
|
-
"test:nats": "asyncapi generate fromTemplate ../examples/nats/asyncapi.yaml ./ -o test-output-nats --force-write && echo 'Generated library files:' && ls -la test-output-nats/ && cd test-output-nats && cargo build --lib"
|
|
24
|
+
"test:nats": "asyncapi generate fromTemplate ../examples/nats/asyncapi.yaml ./ -o test-output-nats --force-write && echo 'Generated library files:' && ls -la test-output-nats/ && cd test-output-nats && cargo build --lib",
|
|
25
|
+
"clean": "rm -rf dist test-output-*"
|
|
22
26
|
},
|
|
27
|
+
"files": [
|
|
28
|
+
"template/**/*",
|
|
29
|
+
"dist/common/**/*",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
23
32
|
"generator": {
|
|
24
33
|
"renderer": "react",
|
|
25
34
|
"apiVersion": "v3",
|
package/template/Cargo.toml.js
CHANGED
|
@@ -1,49 +1,24 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
+
import {
|
|
4
|
+
toKebabCase,
|
|
5
|
+
toSnakeCase,
|
|
6
|
+
isTemplateVariable,
|
|
7
|
+
extractAsyncApiInfo,
|
|
8
|
+
resolveTemplateParameters
|
|
9
|
+
} from './helpers/index.js';
|
|
3
10
|
|
|
4
11
|
module.exports = function ({ asyncapi, params }) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return str.replace(/[^a-zA-Z0-9]/g, '-')
|
|
8
|
-
.toLowerCase()
|
|
9
|
-
.replace(/-+/g, '-')
|
|
10
|
-
.replace(/^-|-$/g, '');
|
|
11
|
-
}
|
|
12
|
+
const asyncApiInfo = extractAsyncApiInfo(asyncapi);
|
|
13
|
+
const { title, version, description } = asyncApiInfo;
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return str.replace(/[^a-zA-Z0-9]/g, '_')
|
|
16
|
-
.toLowerCase()
|
|
17
|
-
.replace(/_+/g, '_')
|
|
18
|
-
.replace(/^_|_$/g, '');
|
|
19
|
-
}
|
|
15
|
+
const resolvedParams = resolveTemplateParameters(params, asyncApiInfo);
|
|
16
|
+
const { packageName, packageVersion, author, license } = resolvedParams;
|
|
20
17
|
|
|
21
|
-
const
|
|
22
|
-
const title = info.title();
|
|
23
|
-
const description = (info.description() || `Generated Rust NATS client for ${title}`)
|
|
18
|
+
const finalDescription = (description || `Generated Rust NATS client for ${title}`)
|
|
24
19
|
.replace(/"/g, '\\"')
|
|
25
20
|
.replace(/\n/g, ' ')
|
|
26
21
|
.trim();
|
|
27
|
-
const version = info.version();
|
|
28
|
-
|
|
29
|
-
// Helper function to check if a parameter contains unresolved template variables
|
|
30
|
-
function isTemplateVariable(value) {
|
|
31
|
-
return typeof value === 'string' && value.includes('{{') && value.includes('}}');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Resolve parameters with fallbacks
|
|
35
|
-
const packageName = (params.packageName && !isTemplateVariable(params.packageName))
|
|
36
|
-
? params.packageName
|
|
37
|
-
: toKebabCase(title) + '-client';
|
|
38
|
-
const packageVersion = (params.packageVersion && !isTemplateVariable(params.packageVersion))
|
|
39
|
-
? params.packageVersion
|
|
40
|
-
: version;
|
|
41
|
-
const author = (params.author && !isTemplateVariable(params.author))
|
|
42
|
-
? params.author
|
|
43
|
-
: 'AsyncAPI Generator';
|
|
44
|
-
const license = (params.license && !isTemplateVariable(params.license))
|
|
45
|
-
? params.license
|
|
46
|
-
: 'Apache-2.0';
|
|
47
22
|
|
|
48
23
|
return (
|
|
49
24
|
<File name="Cargo.toml">
|
|
@@ -53,7 +28,7 @@ version = "${packageVersion}"
|
|
|
53
28
|
edition = "2021"
|
|
54
29
|
authors = ["${author}"]
|
|
55
30
|
license = "${license}"
|
|
56
|
-
description = "${
|
|
31
|
+
description = "${finalDescription}"
|
|
57
32
|
repository = "https://github.com/your-org/${packageName}"
|
|
58
33
|
documentation = "https://docs.rs/${packageName}"
|
|
59
34
|
keywords = ["asyncapi", "nats", "client", "messaging"]
|
|
@@ -1,303 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const parts = identifier
|
|
57
|
-
.replace(/([a-z])([A-Z])/g, '$1_$2') // Insert underscore before capital letters
|
|
58
|
-
.split(/[_\s-]+/) // Split on underscores, spaces, and hyphens
|
|
59
|
-
.filter(part => part.length > 0);
|
|
60
|
-
|
|
61
|
-
return parts
|
|
62
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
63
|
-
.join('');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Converts a string to snake_case Rust field name
|
|
68
|
-
*
|
|
69
|
-
* @param {string} str - Input string to convert
|
|
70
|
-
* @returns {string} snake_case Rust field name
|
|
71
|
-
*/
|
|
72
|
-
export function toRustFieldName(str) {
|
|
73
|
-
if (!str) return 'unknown';
|
|
74
|
-
const identifier = toRustIdentifier(str);
|
|
75
|
-
return identifier
|
|
76
|
-
.replace(/([A-Z])/g, '_$1')
|
|
77
|
-
.toLowerCase()
|
|
78
|
-
.replace(/^_/, '')
|
|
79
|
-
.replace(/_+/g, '_');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Converts a string to Rust enum variant name (PascalCase)
|
|
84
|
-
*
|
|
85
|
-
* @param {string} str - Input string to convert
|
|
86
|
-
* @returns {string} PascalCase enum variant name
|
|
87
|
-
*/
|
|
88
|
-
export function toRustEnumVariant(str) {
|
|
89
|
-
if (!str) return 'Unknown';
|
|
90
|
-
return str
|
|
91
|
-
.split(/[-_\s]+/)
|
|
92
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
93
|
-
.join('');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Converts a string to Rust enum variant with serde rename for lowercase serialization
|
|
98
|
-
*
|
|
99
|
-
* @param {string} str - Input string to convert
|
|
100
|
-
* @returns {object} Object with rustName (PascalCase) and serializedName (lowercase)
|
|
101
|
-
*/
|
|
102
|
-
export function toRustEnumVariantWithSerde(str) {
|
|
103
|
-
if (!str) return { rustName: 'Unknown', serializedName: 'unknown' };
|
|
104
|
-
|
|
105
|
-
const rustName = str
|
|
106
|
-
.split(/[-_\s]+/)
|
|
107
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
108
|
-
.join('');
|
|
109
|
-
|
|
110
|
-
const serializedName = str.toLowerCase();
|
|
111
|
-
|
|
112
|
-
return { rustName, serializedName };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Gets the message type name from a message object
|
|
117
|
-
*
|
|
118
|
-
* @param {object} message - AsyncAPI message object
|
|
119
|
-
* @returns {string|null} Message type name or null if not found
|
|
120
|
-
*/
|
|
121
|
-
export function getMessageTypeName(message) {
|
|
122
|
-
if (!message) return null;
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
// Try AsyncAPI 3.x format first - check _meta and _json properties
|
|
126
|
-
if (message._meta && message._meta.id) {
|
|
127
|
-
return message._meta.id;
|
|
128
|
-
}
|
|
129
|
-
if (message._json && message._json['x-parser-message-name']) {
|
|
130
|
-
return message._json['x-parser-message-name'];
|
|
131
|
-
}
|
|
132
|
-
if (message._json && message._json['x-parser-unique-object-id']) {
|
|
133
|
-
return message._json['x-parser-unique-object-id'];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Try different ways to get the message name
|
|
137
|
-
if (message.name && typeof message.name === 'function') {
|
|
138
|
-
return message.name();
|
|
139
|
-
}
|
|
140
|
-
if (message.name && typeof message.name === 'string') {
|
|
141
|
-
return message.name;
|
|
142
|
-
}
|
|
143
|
-
if (message.title && typeof message.title === 'function') {
|
|
144
|
-
return message.title();
|
|
145
|
-
}
|
|
146
|
-
if (message.title && typeof message.title === 'string') {
|
|
147
|
-
return message.title;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Try to extract from $ref
|
|
151
|
-
if (message.$ref) {
|
|
152
|
-
return message.$ref.split('/').pop();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return null;
|
|
156
|
-
} catch (e) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Gets the proper Rust type name from a message
|
|
163
|
-
*
|
|
164
|
-
* @param {object} message - AsyncAPI message object
|
|
165
|
-
* @returns {string} Rust type name
|
|
166
|
-
*/
|
|
167
|
-
export function getMessageRustTypeName(message) {
|
|
168
|
-
const messageName = getMessageTypeName(message);
|
|
169
|
-
return messageName ? toRustTypeName(messageName) : 'UnknownMessage';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Gets the payload component schema Rust type name from a message
|
|
174
|
-
* This extracts the actual payload type (component schema) rather than the message wrapper
|
|
175
|
-
*
|
|
176
|
-
* @param {object} message - AsyncAPI message object
|
|
177
|
-
* @returns {string} Rust type name for the payload component schema
|
|
178
|
-
*/
|
|
179
|
-
export function getPayloadRustTypeName(message) {
|
|
180
|
-
if (!message) return 'UnknownPayload';
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
// First priority: For inline message schemas, get the message name itself
|
|
184
|
-
// This handles cases where the message is defined inline in channels
|
|
185
|
-
const messageName = getMessageTypeName(message);
|
|
186
|
-
if (messageName) {
|
|
187
|
-
// Check if this is a component message reference (has payload.$ref)
|
|
188
|
-
let payload = null;
|
|
189
|
-
if (message.payload && typeof message.payload === 'function') {
|
|
190
|
-
payload = message.payload();
|
|
191
|
-
} else if (message.payload) {
|
|
192
|
-
payload = message.payload;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Check the message's _json for payload information
|
|
196
|
-
const messageJson = message._json || message;
|
|
197
|
-
if (!payload && messageJson.payload) {
|
|
198
|
-
payload = messageJson.payload;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// If payload has a $ref, this is a component message - extract the schema name
|
|
202
|
-
if (payload && payload.$ref) {
|
|
203
|
-
const refParts = payload.$ref.split('/');
|
|
204
|
-
const schemaName = refParts[refParts.length - 1];
|
|
205
|
-
return toRustTypeName(schemaName);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// For inline message schemas, use the message name directly as the payload type
|
|
209
|
-
// This is the correct approach for messages defined inline in channels
|
|
210
|
-
return toRustTypeName(messageName);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Second priority: Try to get the payload schema reference from the message
|
|
214
|
-
let payload = null;
|
|
215
|
-
|
|
216
|
-
// Try different ways to access the payload
|
|
217
|
-
if (message.payload && typeof message.payload === 'function') {
|
|
218
|
-
payload = message.payload();
|
|
219
|
-
} else if (message.payload) {
|
|
220
|
-
payload = message.payload;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (payload) {
|
|
224
|
-
// Check for $ref in the payload (direct reference to component schema)
|
|
225
|
-
if (payload.$ref) {
|
|
226
|
-
const refParts = payload.$ref.split('/');
|
|
227
|
-
const schemaName = refParts[refParts.length - 1];
|
|
228
|
-
return toRustTypeName(schemaName);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Check for resolved $ref using x-parser-schema-id
|
|
232
|
-
if (payload['x-parser-schema-id']) {
|
|
233
|
-
return toRustTypeName(payload['x-parser-schema-id']);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Check for x-parser-schema-id in _json
|
|
237
|
-
if (payload._json && payload._json['x-parser-schema-id']) {
|
|
238
|
-
return toRustTypeName(payload._json['x-parser-schema-id']);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Check for title or name in the payload schema
|
|
242
|
-
if (payload.title) {
|
|
243
|
-
const title = typeof payload.title === 'function' ? payload.title() : payload.title;
|
|
244
|
-
if (title) return toRustTypeName(title);
|
|
245
|
-
}
|
|
246
|
-
if (payload.name) {
|
|
247
|
-
const name = typeof payload.name === 'function' ? payload.name() : payload.name;
|
|
248
|
-
if (name) return toRustTypeName(name);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Check the message's _json for payload information
|
|
253
|
-
const messageJson = message._json || message;
|
|
254
|
-
if (messageJson.payload) {
|
|
255
|
-
if (messageJson.payload.$ref) {
|
|
256
|
-
const refParts = messageJson.payload.$ref.split('/');
|
|
257
|
-
const schemaName = refParts[refParts.length - 1];
|
|
258
|
-
return toRustTypeName(schemaName);
|
|
259
|
-
}
|
|
260
|
-
if (messageJson.payload['x-parser-schema-id']) {
|
|
261
|
-
return toRustTypeName(messageJson.payload['x-parser-schema-id']);
|
|
262
|
-
}
|
|
263
|
-
if (messageJson.payload.title) {
|
|
264
|
-
return toRustTypeName(messageJson.payload.title);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Final fallback: try to extract from message title or name directly
|
|
269
|
-
if (message.title && typeof message.title === 'function') {
|
|
270
|
-
const title = message.title();
|
|
271
|
-
if (title && typeof title === 'string') {
|
|
272
|
-
return toRustTypeName(title);
|
|
273
|
-
}
|
|
274
|
-
} else if (message.title && typeof message.title === 'string') {
|
|
275
|
-
return toRustTypeName(message.title);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (message.name && typeof message.name === 'function') {
|
|
279
|
-
const name = message.name();
|
|
280
|
-
if (name && typeof name === 'string') {
|
|
281
|
-
return toRustTypeName(name);
|
|
282
|
-
}
|
|
283
|
-
} else if (message.name && typeof message.name === 'string') {
|
|
284
|
-
return toRustTypeName(message.name);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Check message._json for title/name
|
|
288
|
-
if (messageJson.title && typeof messageJson.title === 'string') {
|
|
289
|
-
return toRustTypeName(messageJson.title);
|
|
290
|
-
}
|
|
291
|
-
if (messageJson.name && typeof messageJson.name === 'string') {
|
|
292
|
-
return toRustTypeName(messageJson.name);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return 'UnknownPayload';
|
|
296
|
-
} catch (e) {
|
|
297
|
-
console.warn('Error extracting payload type name:', e.message);
|
|
298
|
-
return 'UnknownPayload';
|
|
299
|
-
}
|
|
300
|
-
}
|
|
2
|
+
* Template-specific helper functions for Rust client template
|
|
3
|
+
*
|
|
4
|
+
* Most common utilities have been moved to the shared @common package.
|
|
5
|
+
* This file now only contains template-specific functions that are unique
|
|
6
|
+
* to the rust-client template.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Import common utilities for use in local functions
|
|
10
|
+
import {
|
|
11
|
+
toRustIdentifier,
|
|
12
|
+
toRustTypeName,
|
|
13
|
+
toRustFieldName,
|
|
14
|
+
toRustEnumVariant,
|
|
15
|
+
toRustEnumVariantWithSerde,
|
|
16
|
+
getMessageTypeName,
|
|
17
|
+
getMessageRustTypeName,
|
|
18
|
+
getPayloadRustTypeName,
|
|
19
|
+
getNatsSubject,
|
|
20
|
+
isDynamicChannel,
|
|
21
|
+
extractChannelVariables,
|
|
22
|
+
getChannelParameters,
|
|
23
|
+
resolveChannelAddress,
|
|
24
|
+
channelHasParameters,
|
|
25
|
+
toPascalCase,
|
|
26
|
+
toKebabCase,
|
|
27
|
+
toSnakeCase,
|
|
28
|
+
isTemplateVariable,
|
|
29
|
+
extractAsyncApiInfo,
|
|
30
|
+
resolveTemplateParameters
|
|
31
|
+
} from "../dist/common/index.js";
|
|
32
|
+
|
|
33
|
+
// Re-export common utilities for backward compatibility
|
|
34
|
+
export {
|
|
35
|
+
toRustIdentifier,
|
|
36
|
+
toRustTypeName,
|
|
37
|
+
toRustFieldName,
|
|
38
|
+
toRustEnumVariant,
|
|
39
|
+
toRustEnumVariantWithSerde,
|
|
40
|
+
getMessageTypeName,
|
|
41
|
+
getMessageRustTypeName,
|
|
42
|
+
getPayloadRustTypeName,
|
|
43
|
+
getNatsSubject,
|
|
44
|
+
isDynamicChannel,
|
|
45
|
+
extractChannelVariables,
|
|
46
|
+
getChannelParameters,
|
|
47
|
+
resolveChannelAddress,
|
|
48
|
+
channelHasParameters,
|
|
49
|
+
toPascalCase,
|
|
50
|
+
toKebabCase,
|
|
51
|
+
toSnakeCase,
|
|
52
|
+
isTemplateVariable,
|
|
53
|
+
extractAsyncApiInfo,
|
|
54
|
+
resolveTemplateParameters
|
|
55
|
+
};
|
|
301
56
|
|
|
302
57
|
/**
|
|
303
58
|
* Analyzes operations to determine client method patterns
|
|
@@ -306,6 +61,8 @@ export function getPayloadRustTypeName(message) {
|
|
|
306
61
|
* - Publish operations (fire-and-forget)
|
|
307
62
|
* - Subscribe operations (message handlers)
|
|
308
63
|
*
|
|
64
|
+
* This function is specific to the rust-client template's operation analysis needs.
|
|
65
|
+
*
|
|
309
66
|
* @param {Array} operations - Array of AsyncAPI operations
|
|
310
67
|
* @returns {Array} Array of client method patterns
|
|
311
68
|
*/
|
|
@@ -357,145 +114,3 @@ export function analyzeClientOperations(operations) {
|
|
|
357
114
|
|
|
358
115
|
return patterns;
|
|
359
116
|
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Gets the NATS subject from a channel address
|
|
363
|
-
*
|
|
364
|
-
* @param {object} channel - AsyncAPI channel object
|
|
365
|
-
* @returns {string} NATS subject
|
|
366
|
-
*/
|
|
367
|
-
export function getNatsSubject(channel) {
|
|
368
|
-
try {
|
|
369
|
-
if (channel.address && typeof channel.address === 'function') {
|
|
370
|
-
return channel.address();
|
|
371
|
-
} else if (channel.address) {
|
|
372
|
-
return channel.address;
|
|
373
|
-
} else if (channel.id && typeof channel.id === 'function') {
|
|
374
|
-
return channel.id();
|
|
375
|
-
} else if (channel.id) {
|
|
376
|
-
return channel.id;
|
|
377
|
-
}
|
|
378
|
-
return 'unknown.subject';
|
|
379
|
-
} catch (e) {
|
|
380
|
-
return 'unknown.subject';
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Checks if a channel address contains variables (dynamic channel)
|
|
386
|
-
*
|
|
387
|
-
* @param {string} address - Channel address
|
|
388
|
-
* @returns {boolean} True if the address contains variables
|
|
389
|
-
*/
|
|
390
|
-
export function isDynamicChannel(address) {
|
|
391
|
-
if (!address || typeof address !== 'string') return false;
|
|
392
|
-
return /\{[^}]+\}/.test(address);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Extracts variable names from a channel address
|
|
397
|
-
*
|
|
398
|
-
* @param {string} address - Channel address with variables
|
|
399
|
-
* @returns {Array<string>} Array of variable names
|
|
400
|
-
*/
|
|
401
|
-
export function extractChannelVariables(address) {
|
|
402
|
-
if (!address || typeof address !== 'string') return [];
|
|
403
|
-
const matches = address.match(/\{([^}]+)\}/g);
|
|
404
|
-
if (!matches) return [];
|
|
405
|
-
return matches.map(match => match.slice(1, -1)); // Remove { and }
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Gets channel parameters from a channel object
|
|
410
|
-
*
|
|
411
|
-
* @param {object} channel - AsyncAPI channel object
|
|
412
|
-
* @returns {Array<object>} Array of parameter objects with name and description
|
|
413
|
-
*/
|
|
414
|
-
export function getChannelParameters(channel) {
|
|
415
|
-
try {
|
|
416
|
-
const parameters = [];
|
|
417
|
-
|
|
418
|
-
// Try to get parameters from the channel
|
|
419
|
-
let channelParams = null;
|
|
420
|
-
if (channel.parameters && typeof channel.parameters === 'function') {
|
|
421
|
-
channelParams = channel.parameters();
|
|
422
|
-
} else if (channel.parameters) {
|
|
423
|
-
channelParams = channel.parameters;
|
|
424
|
-
} else if (channel._json && channel._json.parameters) {
|
|
425
|
-
channelParams = channel._json.parameters;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (channelParams) {
|
|
429
|
-
// Handle different parameter formats
|
|
430
|
-
if (typeof channelParams === 'object') {
|
|
431
|
-
for (const [paramName, paramDef] of Object.entries(channelParams)) {
|
|
432
|
-
// Skip internal AsyncAPI parser properties
|
|
433
|
-
if (paramName.startsWith('_') || paramName === 'collections' || paramName === 'meta') {
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
let description = 'Channel parameter';
|
|
438
|
-
|
|
439
|
-
if (paramDef && typeof paramDef === 'object') {
|
|
440
|
-
if (typeof paramDef.description === 'string') {
|
|
441
|
-
description = paramDef.description;
|
|
442
|
-
} else if (typeof paramDef.description === 'function') {
|
|
443
|
-
try {
|
|
444
|
-
description = paramDef.description();
|
|
445
|
-
} catch (e) {
|
|
446
|
-
description = 'Channel parameter';
|
|
447
|
-
}
|
|
448
|
-
} else if (paramDef._json && paramDef._json.description) {
|
|
449
|
-
description = paramDef._json.description;
|
|
450
|
-
}
|
|
451
|
-
} else if (typeof paramDef === 'string') {
|
|
452
|
-
description = paramDef;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
parameters.push({
|
|
456
|
-
name: paramName,
|
|
457
|
-
description: description,
|
|
458
|
-
rustName: toRustFieldName(paramName),
|
|
459
|
-
rustType: 'String' // For now, assume all parameters are strings
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return parameters;
|
|
466
|
-
} catch (e) {
|
|
467
|
-
console.warn('Error extracting channel parameters:', e.message);
|
|
468
|
-
return [];
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Resolves a dynamic channel address with provided variable values
|
|
474
|
-
*
|
|
475
|
-
* @param {string} address - Channel address template with variables
|
|
476
|
-
* @param {object} variables - Object mapping variable names to values
|
|
477
|
-
* @returns {string} Resolved channel address
|
|
478
|
-
*/
|
|
479
|
-
export function resolveChannelAddress(address, variables) {
|
|
480
|
-
if (!address || typeof address !== 'string') return address;
|
|
481
|
-
if (!variables || typeof variables !== 'object') return address;
|
|
482
|
-
|
|
483
|
-
let resolved = address;
|
|
484
|
-
for (const [varName, varValue] of Object.entries(variables)) {
|
|
485
|
-
const placeholder = `{${varName}}`;
|
|
486
|
-
resolved = resolved.replace(new RegExp(placeholder.replace(/[{}]/g, '\\$&'), 'g'), varValue);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return resolved;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Checks if a channel has dynamic parameters
|
|
494
|
-
*
|
|
495
|
-
* @param {object} channel - AsyncAPI channel object
|
|
496
|
-
* @returns {boolean} True if the channel has parameters
|
|
497
|
-
*/
|
|
498
|
-
export function channelHasParameters(channel) {
|
|
499
|
-
const address = getNatsSubject(channel);
|
|
500
|
-
return isDynamicChannel(address);
|
|
501
|
-
}
|