@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 +262 -0
- package/package.json +52 -0
- package/template/Cargo.toml.js +100 -0
- package/template/helpers/index.js +501 -0
- package/template/index.js +244 -0
- package/template/package.json +48 -0
- package/template/src/auth.rs.js +221 -0
- package/template/src/client.rs.js +681 -0
- package/template/src/envelope.rs.js +199 -0
- package/template/src/errors.rs.js +49 -0
- package/template/src/lib.rs.js +112 -0
- package/template/src/models.rs.js +625 -0
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
|
+
};
|