typespec-rust-emitter 0.1.0
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 +220 -0
- package/dist/src/emitter.d.ts +7 -0
- package/dist/src/emitter.js +490 -0
- package/dist/src/emitter.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib.d.ts +12 -0
- package/dist/src/lib.js +7 -0
- package/dist/src/lib.js.map +1 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.js +8 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/test/hello.test.d.ts +1 -0
- package/dist/test/hello.test.js +140 -0
- package/dist/test/hello.test.js.map +1 -0
- package/dist/test/test-host.d.ts +4 -0
- package/dist/test/test-host.js +16 -0
- package/dist/test/test-host.js.map +1 -0
- package/eslint.config.js +20 -0
- package/example/lib/learning/models.tsp +189 -0
- package/example/lib/learning/operations.tsp +319 -0
- package/example/main.tsp +8 -0
- package/example/output-rust/Cargo.lock +1731 -0
- package/example/output-rust/Cargo.toml +12 -0
- package/example/output-rust/src/generated/mod.rs +1 -0
- package/example/output-rust/src/generated/types.rs +315 -0
- package/example/output-rust/src/main.rs +5 -0
- package/example/output-rust/src/mod.rs +1 -0
- package/example/package-lock.json +1495 -0
- package/example/package.json +15 -0
- package/example/tspconfig.yaml +10 -0
- package/justfile +15 -0
- package/package.json +64 -0
- package/prettierrc.yaml +8 -0
- package/src/emitter.ts +685 -0
- package/src/index.ts +3 -0
- package/src/lib.ts +8 -0
- package/src/lib.tsp +6 -0
- package/src/testing/index.ts +8 -0
- package/test/hello.test.ts +168 -0
- package/test/test-host.ts +20 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Authored by [opencode](https://opencode.ai)
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# TypeSpec Rust Emitter
|
|
6
|
+
|
|
7
|
+
A TypeSpec emitter that generates idiomatic Rust types and structs from TypeSpec specifications.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/typespec-rust-emitter)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Models**: Converts TypeSpec models to Rust structs with serde derive macros
|
|
15
|
+
- **Enums**: Supports both string and integer enums
|
|
16
|
+
- **Unions**: Handles nullable types (`T | null` → `Option<T>`) and string literal unions
|
|
17
|
+
- **Scalars**: Maps TypeSpec scalars to Rust equivalents with `@format` support
|
|
18
|
+
- **Inheritance**: Supports model inheritance with `getAllProperties()`
|
|
19
|
+
- **Error Models**: Generates `thiserror::Error` derive with `#[error(...)]` attributes
|
|
20
|
+
- **Pattern Validation**: Supports `@pattern` decorators with `TryFrom<String>` validation
|
|
21
|
+
- **Custom Derives**: Add arbitrary Rust derive macros via `@rustDerive` decorator
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install typespec-rust-emitter
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Peer Dependencies
|
|
30
|
+
|
|
31
|
+
This package requires the following peer dependencies:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @typespec/compiler @typespec/emitter-framework
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Basic Model
|
|
42
|
+
|
|
43
|
+
```typespec
|
|
44
|
+
import "typespec-emitter";
|
|
45
|
+
|
|
46
|
+
model User {
|
|
47
|
+
name: string;
|
|
48
|
+
age: int32;
|
|
49
|
+
email: string | null;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Generates:
|
|
54
|
+
|
|
55
|
+
```rust
|
|
56
|
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
57
|
+
pub struct User {
|
|
58
|
+
#[serde(rename = "name")]
|
|
59
|
+
pub name: String,
|
|
60
|
+
#[serde(rename = "age")]
|
|
61
|
+
pub age: i32,
|
|
62
|
+
#[serde(rename = "email")]
|
|
63
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
64
|
+
pub email: Option<String>,
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### String Enum
|
|
69
|
+
|
|
70
|
+
```typespec
|
|
71
|
+
enum Status {
|
|
72
|
+
active,
|
|
73
|
+
inactive,
|
|
74
|
+
pending,
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Generates:
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
|
82
|
+
pub enum Status {
|
|
83
|
+
#[serde(rename = "active")]
|
|
84
|
+
Active,
|
|
85
|
+
#[serde(rename = "inactive")]
|
|
86
|
+
Inactive,
|
|
87
|
+
#[serde(rename = "pending")]
|
|
88
|
+
Pending,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
impl Default for Status {
|
|
92
|
+
fn default() -> Self {
|
|
93
|
+
Status::Active
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Error Model
|
|
99
|
+
|
|
100
|
+
```typespec
|
|
101
|
+
@error
|
|
102
|
+
model ApiError {
|
|
103
|
+
code: string;
|
|
104
|
+
message: string;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Generates:
|
|
109
|
+
|
|
110
|
+
```rust
|
|
111
|
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, thiserror::Error)]
|
|
112
|
+
#[error("{code}: {message}")]
|
|
113
|
+
pub struct ApiError {
|
|
114
|
+
#[serde(rename = "code")]
|
|
115
|
+
pub code: String,
|
|
116
|
+
#[serde(rename = "message")]
|
|
117
|
+
pub message: String,
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### UUID Format
|
|
122
|
+
|
|
123
|
+
```typespec
|
|
124
|
+
@format("uuid")
|
|
125
|
+
scalar Uuid extends string;
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Generates:
|
|
129
|
+
|
|
130
|
+
```rust
|
|
131
|
+
pub type Uuid = uuid::Uuid;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Pattern Validation
|
|
135
|
+
|
|
136
|
+
```typespec
|
|
137
|
+
@pattern("^#[0-9A-Fa-f]{6}$")
|
|
138
|
+
scalar HexColor extends string;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Generates:
|
|
142
|
+
|
|
143
|
+
```rust
|
|
144
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
|
145
|
+
pub struct HexColor(pub String);
|
|
146
|
+
|
|
147
|
+
impl TryFrom<String> for HexColor {
|
|
148
|
+
type Error = String;
|
|
149
|
+
|
|
150
|
+
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
151
|
+
let re = regex::Regex::new(r"^#[0-9A-Fa-f]{6}$").unwrap();
|
|
152
|
+
if re.is_match(&value) { Ok(Self(value)) } else { Err(format!("Invalid value: {}", value)) }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Custom Derives
|
|
158
|
+
|
|
159
|
+
```typespec
|
|
160
|
+
import "typespec-emitter";
|
|
161
|
+
|
|
162
|
+
@rustDerive("sqlx::FromRow")
|
|
163
|
+
@rustDerive("derive_more::Display")
|
|
164
|
+
model GroupStatistics {
|
|
165
|
+
id: int64;
|
|
166
|
+
name: string;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Generates:
|
|
171
|
+
|
|
172
|
+
```rust
|
|
173
|
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow, derive_more::Display)]
|
|
174
|
+
pub struct GroupStatistics {
|
|
175
|
+
#[serde(rename = "id")]
|
|
176
|
+
pub id: i64,
|
|
177
|
+
#[serde(rename = "name")]
|
|
178
|
+
pub name: String,
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Type Mappings
|
|
183
|
+
|
|
184
|
+
| TypeSpec Type | Rust Type |
|
|
185
|
+
| ---------------- | ------------------------------------------- |
|
|
186
|
+
| `string` | `String` |
|
|
187
|
+
| `int32` | `i32` |
|
|
188
|
+
| `int64` | `i64` |
|
|
189
|
+
| `float32` | `f32` |
|
|
190
|
+
| `float64` | `f64` |
|
|
191
|
+
| `boolean` | `bool` |
|
|
192
|
+
| `string[]` | `Vec<String>` |
|
|
193
|
+
| `Record<string>` | `std::collections::HashMap<String, String>` |
|
|
194
|
+
| `T \| null` | `Option<T>` |
|
|
195
|
+
|
|
196
|
+
## Building
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm run build
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Testing
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm test
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
The emitter is built using the TypeSpec emitter framework. Key files:
|
|
211
|
+
|
|
212
|
+
- `src/emitter.ts` - Main emitter implementation
|
|
213
|
+
- `src/lib.tsp` - TypeSpec decorator declarations
|
|
214
|
+
- `src/index.ts` - Public exports
|
|
215
|
+
- `test/hello.test.ts` - Unit tests
|
|
216
|
+
- `example/` - Example TypeSpec definitions and generated Rust output
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { EmitContext, Type, DecoratorContext } from "@typespec/compiler";
|
|
2
|
+
export interface RustEmitterOptions {
|
|
3
|
+
moduleName?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function $rustDerive(context: DecoratorContext, target: Type, derive: string): void;
|
|
6
|
+
export declare function $rustDerives(context: DecoratorContext, target: Type, ...derives: string[]): void;
|
|
7
|
+
export declare function $onEmit(context: EmitContext<RustEmitterOptions>, _options?: RustEmitterOptions): Promise<void>;
|