@ioka-technologies/asyncapi-rust-client-template 0.0.20

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/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # AsyncAPI Rust NATS Client Template
2
+
3
+ This template generates a Rust client library for NATS messaging based on AsyncAPI specifications. It creates type-safe, idiomatic Rust code that integrates seamlessly with the `async-nats` ecosystem.
4
+
5
+ ## Features
6
+
7
+ - **Type-Safe Client Generation**: Generates Rust structs and client methods from AsyncAPI schemas
8
+ - **NATS Integration**: Uses the official `async-nats` client library
9
+ - **Request/Reply Support**: Automatically detects and implements NATS request/reply patterns
10
+ - **Pub/Sub Support**: Supports NATS publish/subscribe patterns for asynchronous messaging
11
+ - **Message Envelope**: Consistent message format with metadata and correlation IDs
12
+ - **Flexible Authentication**: Accepts pre-configured NATS clients with any authentication method
13
+ - **Comprehensive Error Handling**: Specific error types for different failure scenarios
14
+ - **Documentation**: Generates comprehensive documentation and usage examples
15
+
16
+ ## Usage
17
+
18
+ ### Prerequisites
19
+
20
+ - AsyncAPI Generator CLI
21
+ - AsyncAPI specification with NATS protocol
22
+ - Rust 1.70+ (for generated code)
23
+
24
+ ### Generate a Client
25
+
26
+ ```bash
27
+ # Install AsyncAPI Generator if not already installed
28
+ npm install -g @asyncapi/generator
29
+
30
+ # Generate Rust NATS client
31
+ ag path/to/your/asyncapi.yaml @asyncapi/rust-client-template -o ./generated-client
32
+ ```
33
+
34
+ ### Template Parameters
35
+
36
+ The template supports several parameters to customize the generated code:
37
+
38
+ - `clientName`: Name of the generated client struct (default: `{Title}Client`)
39
+ - `packageName`: Name of the generated Rust crate (default: `{title}-client`)
40
+ - `packageVersion`: Version of the generated crate (default: from AsyncAPI spec)
41
+ - `author`: Author of the generated crate (default: "AsyncAPI Generator")
42
+ - `license`: License of the generated crate (default: "Apache-2.0")
43
+
44
+ Example with parameters:
45
+
46
+ ```bash
47
+ ag asyncapi.yaml @asyncapi/rust-client-template \
48
+ -o ./my-client \
49
+ -p clientName=MyServiceClient \
50
+ -p packageName=my-service-client \
51
+ -p author="Your Name"
52
+ ```
53
+
54
+ ## Generated Code Structure
55
+
56
+ The template generates a complete Rust crate with the following structure:
57
+
58
+ ```
59
+ generated-client/
60
+ ├── Cargo.toml # Rust package manifest
61
+ ├── README.md # Usage documentation
62
+ └── src/
63
+ ├── lib.rs # Main library file with re-exports
64
+ ├── client.rs # Generated client implementation
65
+ ├── models.rs # Generated data models from schemas
66
+ ├── envelope.rs # Message envelope for consistent format
67
+ └── errors.rs # Error types and handling
68
+ ```
69
+
70
+ ## AsyncAPI Requirements
71
+
72
+ ### Supported Protocols
73
+
74
+ - `nats` - NATS messaging protocol
75
+
76
+ ### Operation Patterns
77
+
78
+ The template supports the following AsyncAPI operation patterns:
79
+
80
+ #### Request/Reply Operations
81
+
82
+ Operations with `action: send` and a `reply` section generate request/reply methods:
83
+
84
+ ```yaml
85
+ operations:
86
+ createUser:
87
+ action: send
88
+ channel:
89
+ $ref: '#/channels/user.create'
90
+ messages:
91
+ - $ref: '#/components/messages/CreateUserRequest'
92
+ reply:
93
+ channel:
94
+ $ref: '#/channels/user.create.reply'
95
+ messages:
96
+ - $ref: '#/components/messages/CreateUserResponse'
97
+ ```
98
+
99
+ Generates:
100
+ ```rust
101
+ pub async fn create_user(&self, payload: CreateUserRequest) -> ClientResult<CreateUserResponse>
102
+ ```
103
+
104
+ #### Publish Operations
105
+
106
+ Operations with `action: send` and no `reply` section generate publish methods:
107
+
108
+ ```yaml
109
+ operations:
110
+ publishUserEvent:
111
+ action: send
112
+ channel:
113
+ $ref: '#/channels/user.events'
114
+ messages:
115
+ - $ref: '#/components/messages/UserEvent'
116
+ ```
117
+
118
+ Generates:
119
+ ```rust
120
+ pub async fn publish_user_event(&self, payload: UserEvent) -> ClientResult<()>
121
+ ```
122
+
123
+ #### Subscribe Operations
124
+
125
+ Operations with `action: receive` generate subscription methods:
126
+
127
+ ```yaml
128
+ operations:
129
+ subscribeUserEvents:
130
+ action: receive
131
+ channel:
132
+ $ref: '#/channels/user.events'
133
+ messages:
134
+ - $ref: '#/components/messages/UserEvent'
135
+ ```
136
+
137
+ Generates:
138
+ ```rust
139
+ pub async fn subscribe_user_events(&self) -> ClientResult<async_nats::Subscriber>
140
+ ```
141
+
142
+ ### Schema Support
143
+
144
+ The template generates Rust structs from AsyncAPI schemas with:
145
+
146
+ - **Type Mapping**: JSON Schema types mapped to appropriate Rust types
147
+ - **Serde Integration**: Automatic serialization/deserialization
148
+ - **Optional Fields**: Proper handling of optional vs required fields
149
+ - **Documentation**: Generated from schema descriptions
150
+ - **Constructors**: Convenience methods for creating instances
151
+
152
+ ## Generated Client Usage
153
+
154
+ ### Basic Usage
155
+
156
+ ```rust
157
+ use async_nats;
158
+ use my_service_client::MyServiceClient;
159
+
160
+ #[tokio::main]
161
+ async fn main() -> Result<(), Box<dyn std::error::Error>> {
162
+ // Set up NATS client
163
+ let nats_client = async_nats::connect("nats://localhost:4222").await?;
164
+
165
+ // Create service client
166
+ let client = MyServiceClient::with(nats_client);
167
+
168
+ // Use generated methods
169
+ let response = client.create_user(CreateUserRequest {
170
+ email: "user@example.com".to_string(),
171
+ name: "John Doe".to_string(),
172
+ }).await?;
173
+
174
+ println!("Created user: {:?}", response);
175
+
176
+ Ok(())
177
+ }
178
+ ```
179
+
180
+ ### With Authentication
181
+
182
+ ```rust
183
+ use async_nats;
184
+ use my_service_client::MyServiceClient;
185
+
186
+ #[tokio::main]
187
+ async fn main() -> Result<(), Box<dyn std::error::Error>> {
188
+ // Set up NATS client with JWT authentication
189
+ let nats_client = async_nats::ConnectOptions::new()
190
+ .credentials_file("./service.creds").await?
191
+ .name("my-service-client")
192
+ .connect("nats://production.example.com:4222").await?;
193
+
194
+ let client = MyServiceClient::with(nats_client);
195
+
196
+ // Client operations work the same way
197
+ let response = client.create_user(request).await?;
198
+
199
+ Ok(())
200
+ }
201
+ ```
202
+
203
+ ### Subscription Handling
204
+
205
+ ```rust
206
+ use async_nats;
207
+ use my_service_client::{MyServiceClient, MessageEnvelope};
208
+ use futures::StreamExt;
209
+
210
+ #[tokio::main]
211
+ async fn main() -> Result<(), Box<dyn std::error::Error>> {
212
+ let nats_client = async_nats::connect("nats://localhost:4222").await?;
213
+ let client = MyServiceClient::with(nats_client);
214
+
215
+ // Subscribe to events
216
+ let mut subscriber = client.subscribe_user_events().await?;
217
+
218
+ // Handle incoming messages
219
+ while let Some(message) = subscriber.next().await {
220
+ let envelope = MessageEnvelope::from_bytes(&message.payload)?;
221
+ let event: UserEvent = envelope.extract_payload()?;
222
+
223
+ println!("Received event: {:?}", event);
224
+
225
+ // Acknowledge message if needed
226
+ message.ack().await?;
227
+ }
228
+
229
+ Ok(())
230
+ }
231
+ ```
232
+
233
+ ## Dependencies
234
+
235
+ The generated client depends on:
236
+
237
+ - `async-nats` - Official async NATS client
238
+ - `serde` - Serialization framework
239
+ - `serde_json` - JSON serialization
240
+ - `uuid` - UUID generation for message IDs
241
+ - `chrono` - Date/time handling
242
+ - `thiserror` - Error handling
243
+
244
+ ## Compatibility
245
+
246
+ - **AsyncAPI**: 2.x and 3.x
247
+ - **Rust**: 1.70+
248
+ - **NATS**: Compatible with NATS 2.x servers
249
+ - **async-nats**: 0.33+
250
+
251
+ ## Contributing
252
+
253
+ 1. Fork the repository
254
+ 2. Create a feature branch
255
+ 3. Make your changes
256
+ 4. Add tests for new functionality
257
+ 5. Run the test suite
258
+ 6. Submit a pull request
259
+
260
+ ## License
261
+
262
+ Apache-2.0
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@ioka-technologies/asyncapi-rust-client-template",
3
+ "version": "0.0.20",
4
+ "description": "AsyncAPI template for generating Rust NATS clients",
5
+ "main": "template/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
+ "scripts": {
20
+ "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"
22
+ },
23
+ "generator": {
24
+ "renderer": "react",
25
+ "apiVersion": "v3",
26
+ "supportedProtocols": [
27
+ "nats"
28
+ ],
29
+ "parameters": {
30
+ "clientName": {
31
+ "description": "Name of the generated client struct",
32
+ "default": "{{asyncapi.info().title() | replace(/[^a-zA-Z0-9]/g, '') }}Client"
33
+ },
34
+ "packageName": {
35
+ "description": "Name of the generated Rust crate",
36
+ "default": "{{asyncapi.info().title() | kebabCase}}-client"
37
+ },
38
+ "packageVersion": {
39
+ "description": "Version of the generated crate",
40
+ "default": "{{asyncapi.info().version()}}"
41
+ },
42
+ "author": {
43
+ "description": "Author of the generated crate",
44
+ "default": "AsyncAPI Generator"
45
+ },
46
+ "license": {
47
+ "description": "License of the generated crate",
48
+ "default": "Apache-2.0"
49
+ }
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,100 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import { File } from '@asyncapi/generator-react-sdk';
3
+
4
+ module.exports = function ({ asyncapi, params }) {
5
+ // Helper function to convert title to kebab-case
6
+ function toKebabCase(str) {
7
+ return str.replace(/[^a-zA-Z0-9]/g, '-')
8
+ .toLowerCase()
9
+ .replace(/-+/g, '-')
10
+ .replace(/^-|-$/g, '');
11
+ }
12
+
13
+ // Helper function to convert title to snake_case
14
+ function toSnakeCase(str) {
15
+ return str.replace(/[^a-zA-Z0-9]/g, '_')
16
+ .toLowerCase()
17
+ .replace(/_+/g, '_')
18
+ .replace(/^_|_$/g, '');
19
+ }
20
+
21
+ const info = asyncapi.info();
22
+ const title = info.title();
23
+ const description = (info.description() || `Generated Rust NATS client for ${title}`)
24
+ .replace(/"/g, '\\"')
25
+ .replace(/\n/g, ' ')
26
+ .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
+
48
+ return (
49
+ <File name="Cargo.toml">
50
+ {`[package]
51
+ name = "${packageName}"
52
+ version = "${packageVersion}"
53
+ edition = "2021"
54
+ authors = ["${author}"]
55
+ license = "${license}"
56
+ description = "${description}"
57
+ repository = "https://github.com/your-org/${packageName}"
58
+ documentation = "https://docs.rs/${packageName}"
59
+ keywords = ["asyncapi", "nats", "client", "messaging"]
60
+ categories = ["network-programming", "api-bindings"]
61
+
62
+ [dependencies]
63
+ # Core async runtime
64
+ tokio = { version = "1.0", features = ["full"] }
65
+
66
+ # NATS client
67
+ async-nats = "0.38"
68
+
69
+ # Serialization
70
+ serde = { version = "1.0", features = ["derive"] }
71
+ serde_json = "1.0"
72
+
73
+ # Utilities
74
+ uuid = { version = "1.0", features = ["v4", "serde"] }
75
+ chrono = { version = "0.4", features = ["serde"] }
76
+ bytes = "1.0"
77
+
78
+ # Authentication support
79
+ base64 = "0.21"
80
+
81
+ # Error handling
82
+ thiserror = "1.0"
83
+
84
+ [dev-dependencies]
85
+ tokio-test = "0.4"
86
+
87
+ [features]
88
+ default = []
89
+
90
+ # Optional features for different authentication methods
91
+ jwt = []
92
+ oauth = []
93
+
94
+ [package.metadata.docs.rs]
95
+ all-features = true
96
+ rustdoc-args = ["--cfg", "docsrs"]
97
+ `}
98
+ </File>
99
+ );
100
+ };