@ioka-technologies/asyncapi-rust-client-template 0.0.33 → 0.0.35
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/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.35",
|
|
4
4
|
"description": "AsyncAPI template for generating Rust NATS clients",
|
|
5
5
|
"main": "template/index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"template/**/*",
|
|
29
|
+
"!template/**/*.dev",
|
|
29
30
|
"dist/common/**/*",
|
|
30
31
|
"README.md"
|
|
31
32
|
],
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
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 '../../../common/src/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
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Analyzes operations to determine client method patterns
|
|
59
|
-
* For NATS clients, we need to distinguish between:
|
|
60
|
-
* - Request/Reply operations (using NATS request/reply)
|
|
61
|
-
* - Publish operations (fire-and-forget)
|
|
62
|
-
* - Subscribe operations (message handlers)
|
|
63
|
-
*
|
|
64
|
-
* This function is specific to the rust-client template's operation analysis needs.
|
|
65
|
-
*
|
|
66
|
-
* @param {Array} operations - Array of AsyncAPI operations
|
|
67
|
-
* @returns {Array} Array of client method patterns
|
|
68
|
-
*/
|
|
69
|
-
export function analyzeClientOperations(operations) {
|
|
70
|
-
const patterns = [];
|
|
71
|
-
|
|
72
|
-
for (const operation of operations) {
|
|
73
|
-
const operationName = operation.id();
|
|
74
|
-
const action = operation.action();
|
|
75
|
-
const messages = operation.messages();
|
|
76
|
-
|
|
77
|
-
if (action === 'send') {
|
|
78
|
-
// Client sends messages - this becomes a client method
|
|
79
|
-
if (operation.reply && operation.reply()) {
|
|
80
|
-
// Request/Reply pattern
|
|
81
|
-
patterns.push({
|
|
82
|
-
type: 'request_reply',
|
|
83
|
-
operation,
|
|
84
|
-
operationName,
|
|
85
|
-
methodName: toRustFieldName(operationName),
|
|
86
|
-
requestMessage: messages[0],
|
|
87
|
-
responseMessage: operation.reply().messages()[0],
|
|
88
|
-
requestType: getPayloadRustTypeName(messages[0]),
|
|
89
|
-
responseType: getPayloadRustTypeName(operation.reply().messages()[0])
|
|
90
|
-
});
|
|
91
|
-
} else {
|
|
92
|
-
// Publish pattern (fire-and-forget)
|
|
93
|
-
patterns.push({
|
|
94
|
-
type: 'publish',
|
|
95
|
-
operation,
|
|
96
|
-
operationName,
|
|
97
|
-
methodName: toRustFieldName(operationName),
|
|
98
|
-
message: messages[0],
|
|
99
|
-
payloadType: getPayloadRustTypeName(messages[0])
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
} else if (action === 'receive') {
|
|
103
|
-
// Client receives messages - this becomes a subscription method
|
|
104
|
-
patterns.push({
|
|
105
|
-
type: 'subscribe',
|
|
106
|
-
operation,
|
|
107
|
-
operationName,
|
|
108
|
-
methodName: toRustFieldName(operationName.replace(/^receive/, 'subscribe_to_')),
|
|
109
|
-
message: messages[0],
|
|
110
|
-
payloadType: getPayloadRustTypeName(messages[0])
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return patterns;
|
|
116
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
|
-
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
-
import { generateMessageEnvelope } from '../../../common/src/index.js';
|
|
4
|
-
|
|
5
|
-
export default function ({ asyncapi, params }) {
|
|
6
|
-
// Generate the unified message envelope with error support
|
|
7
|
-
const envelopeCode = generateMessageEnvelope();
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<File name="envelope.rs">
|
|
11
|
-
{`//! Message envelope for consistent NATS message format
|
|
12
|
-
|
|
13
|
-
use crate::auth::{AuthCredentials, generate_auth_headers};
|
|
14
|
-
${envelopeCode}
|
|
15
|
-
|
|
16
|
-
// Client-specific extensions for auth integration
|
|
17
|
-
impl MessageEnvelope {
|
|
18
|
-
/// Create a new message envelope with authentication headers
|
|
19
|
-
pub fn new_with_auth<T: Serialize>(
|
|
20
|
-
operation: &str,
|
|
21
|
-
payload: T,
|
|
22
|
-
auth: &AuthCredentials,
|
|
23
|
-
) -> Result<Self, serde_json::Error> {
|
|
24
|
-
let auth_headers = generate_auth_headers(auth);
|
|
25
|
-
Self::new(operation, payload).map(|envelope| envelope.with_auth_headers(auth_headers))
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
`}
|
|
29
|
-
</File>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
|
-
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
-
import {
|
|
4
|
-
toRustIdentifier,
|
|
5
|
-
toRustTypeName,
|
|
6
|
-
toRustFieldName,
|
|
7
|
-
toRustEnumVariant,
|
|
8
|
-
toRustEnumVariantWithSerde,
|
|
9
|
-
generateRustModels,
|
|
10
|
-
generateMessageEnvelope
|
|
11
|
-
} from '../../../common/src/index.js';
|
|
12
|
-
|
|
13
|
-
export default function ModelsRs({ asyncapi }) {
|
|
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
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Generate the unified message envelope
|
|
24
|
-
const envelopeCode = generateMessageEnvelope();
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<File name="models.rs">
|
|
28
|
-
{`//! Generated data models from AsyncAPI specification
|
|
29
|
-
|
|
30
|
-
${envelopeCode}
|
|
31
|
-
${models.generateComponentSchemas()}
|
|
32
|
-
${models.generateNestedTypes()}
|
|
33
|
-
${(() => {
|
|
34
|
-
// Track which types have already had implementations generated
|
|
35
|
-
const implementedTypes = new Set();
|
|
36
|
-
const implementations = [];
|
|
37
|
-
|
|
38
|
-
models.messageSchemas.forEach(schema => {
|
|
39
|
-
const doc = schema.description ? `/// ${schema.description}` : `/// ${schema.name} message`;
|
|
40
|
-
|
|
41
|
-
// Check if the message payload references a component schema
|
|
42
|
-
let payloadRustName = null;
|
|
43
|
-
let isComponentMessage = false;
|
|
44
|
-
|
|
45
|
-
if (schema.rawPayload && schema.rawPayload.$ref) {
|
|
46
|
-
const refName = schema.rawPayload.$ref.split('/').pop();
|
|
47
|
-
payloadRustName = toRustTypeName(refName);
|
|
48
|
-
isComponentMessage = true;
|
|
49
|
-
} else if (schema.payload && schema.payload.$ref) {
|
|
50
|
-
const refName = schema.payload.$ref.split('/').pop();
|
|
51
|
-
payloadRustName = toRustTypeName(refName);
|
|
52
|
-
isComponentMessage = true;
|
|
53
|
-
} else if (schema.payload && schema.payload['x-parser-schema-id']) {
|
|
54
|
-
// Handle resolved $ref references
|
|
55
|
-
const schemaId = schema.payload['x-parser-schema-id'];
|
|
56
|
-
if (models.schemaRegistry.has(schemaId)) {
|
|
57
|
-
payloadRustName = toRustTypeName(schemaId);
|
|
58
|
-
isComponentMessage = true;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// For component messages, always generate the message wrapper type
|
|
63
|
-
// even if the payload schema already exists
|
|
64
|
-
if (isComponentMessage && payloadRustName && !implementedTypes.has(schema.rustName)) {
|
|
65
|
-
implementedTypes.add(schema.rustName);
|
|
66
|
-
implementations.push(`
|
|
67
|
-
${doc}
|
|
68
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
69
|
-
pub struct ${schema.rustName} {
|
|
70
|
-
#[serde(flatten)]
|
|
71
|
-
pub payload: ${payloadRustName},
|
|
72
|
-
}`);
|
|
73
|
-
} else if (!models.generatedTypes.has(schema.rustName) && !implementedTypes.has(schema.rustName)) {
|
|
74
|
-
// Generate both struct for inline message schemas
|
|
75
|
-
implementedTypes.add(schema.rustName);
|
|
76
|
-
implementations.push(`
|
|
77
|
-
${doc}
|
|
78
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
79
|
-
pub struct ${schema.rustName} {
|
|
80
|
-
${models.generateMessageStruct(schema.payload, schema.rustName)}
|
|
81
|
-
}`);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
return implementations.join('');
|
|
86
|
-
})()}
|
|
87
|
-
|
|
88
|
-
${models.messageSchemas.length === 0 && models.componentSchemas.length === 0 ? `
|
|
89
|
-
/// Example message structure when no messages are defined in the spec
|
|
90
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
91
|
-
pub struct ExampleMessage {
|
|
92
|
-
pub id: String,
|
|
93
|
-
pub content: String,
|
|
94
|
-
pub timestamp: chrono::DateTime<chrono::Utc>,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
impl ExampleMessage {
|
|
98
|
-
/// Create a new instance with required fields
|
|
99
|
-
pub fn new(id: String, content: String, timestamp: chrono::DateTime<chrono::Utc>) -> Self {
|
|
100
|
-
Self {
|
|
101
|
-
id,
|
|
102
|
-
content,
|
|
103
|
-
timestamp,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}` : ''}
|
|
107
|
-
`}
|
|
108
|
-
</File>
|
|
109
|
-
);
|
|
110
|
-
}
|