typespec-rust-emitter 0.6.0 → 0.8.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/.qwen/settings.json +3 -2
- package/AGENTS.md +167 -0
- package/CHANGELOG.md +76 -0
- package/README.md +1 -1
- package/dist/src/emitter.js +43 -6
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/testing/index.js +1 -1
- package/dist/src/testing/index.js.map +1 -1
- package/example/output-rust/src/generated/server.rs +71 -70
- package/example/output-rust/src/generated/types.rs +28 -0
- package/example/package-lock.json +1 -1
- package/package.json +1 -1
- package/src/emitter.ts +43 -6
- package/src/testing/index.ts +9 -5
- package/DEV.md +0 -81
- package/QWEN.md +0 -292
package/QWEN.md
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
# QWEN.md - Project Context
|
|
2
|
-
|
|
3
|
-
## Project Overview
|
|
4
|
-
|
|
5
|
-
**TypeSpec Rust Emitter** - A TypeScript-based code generator that converts TypeSpec specifications into idiomatic Rust code.
|
|
6
|
-
|
|
7
|
-
- **Type**: TypeScript/Node.js project (TypeSpec emitter)
|
|
8
|
-
- **Output**: Generates Rust structs, enums, server traits with serde, thiserror, axum support
|
|
9
|
-
- **Main Entry**: `src/emitter.ts` - core codegen logic
|
|
10
|
-
- **Package**: `typespec-rust-emitter` v0.3.0
|
|
11
|
-
|
|
12
|
-
## Directory Structure
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
typespec-emitter/
|
|
16
|
-
├── src/
|
|
17
|
-
│ ├── emitter.ts # MAIN: TypeSpec→Rust code generation
|
|
18
|
-
│ ├── index.ts # Exports ($onEmit, $rustDerive, $rustDerives, $rustAttr, $rustAttrs)
|
|
19
|
-
│ ├── lib.ts # TypeSpec library definition
|
|
20
|
-
│ ├── lib.tsp # Decorator declarations
|
|
21
|
-
│ └── testing/ # Test utilities
|
|
22
|
-
├── test/
|
|
23
|
-
│ ├── hello.test.ts # Unit tests
|
|
24
|
-
│ └── test-host.ts # Test infrastructure (emit() helper)
|
|
25
|
-
├── example/
|
|
26
|
-
│ ├── main.tsp # Demo TypeSpec entry
|
|
27
|
-
│ ├── lib/learning/ # Demo models & operations
|
|
28
|
-
│ ├── tspconfig.yaml # Emitter configuration
|
|
29
|
-
│ └── output-rust/ # Generated Rust crate
|
|
30
|
-
├── oas3-gen/ # Reference template repo
|
|
31
|
-
├── dist/ # Compiled TypeScript output
|
|
32
|
-
├── package.json # Dependencies & scripts
|
|
33
|
-
├── tsconfig.json # TypeScript config (strict mode)
|
|
34
|
-
└── justfile # Build automation
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Build & Run Commands
|
|
38
|
-
|
|
39
|
-
### Primary Workflow (Run After Every Change)
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
just build # Compile TypeScript (npm run build)
|
|
43
|
-
just test # Run unit tests (npm test)
|
|
44
|
-
just compile # Generate Rust code from example
|
|
45
|
-
just check-rust # Verify Rust compiles (cargo check)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Individual Commands
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# Development
|
|
52
|
-
npm run build # tsc compile
|
|
53
|
-
npm run watch # Watch mode
|
|
54
|
-
npm run lint # ESLint check
|
|
55
|
-
npm run lint:fix # Auto-fix lint
|
|
56
|
-
npm run format # Prettier format
|
|
57
|
-
npm run format:check
|
|
58
|
-
|
|
59
|
-
# Full pipeline
|
|
60
|
-
just install # npm install (root + example)
|
|
61
|
-
just publish # build + npm publish
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Type Mappings (TypeSpec → Rust)
|
|
65
|
-
|
|
66
|
-
| TypeSpec | Rust |
|
|
67
|
-
| ---------------- | -------------------- |
|
|
68
|
-
| `string` | `String` |
|
|
69
|
-
| `int8/16/32/64` | `i8/i16/i32/i64` |
|
|
70
|
-
| `uint8/16/32/64` | `u8/u16/u32/u64` |
|
|
71
|
-
| `float32/64` | `f32/f64` |
|
|
72
|
-
| `boolean` | `bool` |
|
|
73
|
-
| `bytes` | `Vec<u8>` |
|
|
74
|
-
| `T[]` | `Vec<T>` |
|
|
75
|
-
| `Record<T>` | `HashMap<String, T>` |
|
|
76
|
-
| `T \| null` | `Option<T>` |
|
|
77
|
-
|
|
78
|
-
### Format Mappings
|
|
79
|
-
|
|
80
|
-
| `@format()` | Rust Type |
|
|
81
|
-
| ------------- | ----------------------- |
|
|
82
|
-
| `"uuid"` | `uuid::Uuid` |
|
|
83
|
-
| `"date"` | `chrono::NaiveDate` |
|
|
84
|
-
| `"time"` | `chrono::NaiveTime` |
|
|
85
|
-
| `"date-time"` | `chrono::DateTime<Utc>` |
|
|
86
|
-
|
|
87
|
-
## Decorators
|
|
88
|
-
|
|
89
|
-
| Decorator | Effect |
|
|
90
|
-
| ---------------------------- | ---------------------------------------------------------------------------- |
|
|
91
|
-
| `@error` | Adds `thiserror::Error` derive + `#[error("{code}: {message}")]` |
|
|
92
|
-
| `@pattern("regex")` | Generates `TryFrom<String>` with regex validation |
|
|
93
|
-
| `@rustDerive("...")` | Adds custom derive (models & enums, e.g., `sqlx::FromRow`, `sqlx::Type`) |
|
|
94
|
-
| `@rustDerives("...", "...")` | Adds multiple custom derives (models & enums) |
|
|
95
|
-
| `@rustAttr("...")` | Adds custom Rust attribute (models & enums, e.g., `sqlx(type_name = "...")`) |
|
|
96
|
-
| `@rustAttrs("...", "...")` | Adds multiple custom attributes (models & enums) |
|
|
97
|
-
| `@doc("...")` | Generates `///` doc comments |
|
|
98
|
-
| `@useAuth(BearerAuth)` | Marks operation as protected (adds `Claims` param) |
|
|
99
|
-
| `@maxLength(n)` | Documentation only |
|
|
100
|
-
|
|
101
|
-
## Output Files
|
|
102
|
-
|
|
103
|
-
### types.rs
|
|
104
|
-
|
|
105
|
-
- All models → `pub struct` with serde derives
|
|
106
|
-
- Enums → `pub enum` with serde rename
|
|
107
|
-
- Unions → `Option<T>` for nullable, or sum types
|
|
108
|
-
- Scalars → `pub type` or `pub struct` (if pattern validation)
|
|
109
|
-
|
|
110
|
-
### server.rs
|
|
111
|
-
|
|
112
|
-
- `Server` trait with `type Claims` associated type
|
|
113
|
-
- Request structs per operation
|
|
114
|
-
- Response enums with status code variants
|
|
115
|
-
- `create_router<S, M>(service: S, middleware: M)` function with axum routes
|
|
116
|
-
|
|
117
|
-
## Key Implementation Details
|
|
118
|
-
|
|
119
|
-
### emitter.ts Architecture
|
|
120
|
-
|
|
121
|
-
1. `navigateProgram()` - Walks TypeSpec AST
|
|
122
|
-
2. Collects: models, enums, unions, scalars, operations
|
|
123
|
-
3. Processes decorators (`@error`, `@pattern`, `@rustDerive`)
|
|
124
|
-
4. Generates Rust via string templates
|
|
125
|
-
5. Emits files to configured output directory
|
|
126
|
-
|
|
127
|
-
### Server Trait Generation
|
|
128
|
-
|
|
129
|
-
- Generates `Server` trait with `type Claims: Send + Sync + 'static` associated type
|
|
130
|
-
- Handler functions accept individual parameters (path params, body) directly
|
|
131
|
-
- Path params extracted with `Path<T>`
|
|
132
|
-
- Body extracted with `Json<T>`
|
|
133
|
-
- Protected routes (`@useAuth`) receive `claims: Self::Claims` via `Extension<Self::Claims>`
|
|
134
|
-
- Extractor order: `State` → `Extension` → `Path` → `Json`
|
|
135
|
-
- `create_router<S, M>(service: S, middleware: M)` accepts middleware function
|
|
136
|
-
- Middleware wraps protected routes: `middleware(protected)`
|
|
137
|
-
- Router merges public/protected, applies `.with_state(service)` once at end
|
|
138
|
-
|
|
139
|
-
### Test Pattern
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { emit } from "./test-host.js";
|
|
143
|
-
const results = await emit(`model User { name: string; }`);
|
|
144
|
-
const output = results["types.rs"];
|
|
145
|
-
strictEqual(output.includes("pub struct User"), true);
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Dependencies
|
|
149
|
-
|
|
150
|
-
### Peer (Required)
|
|
151
|
-
|
|
152
|
-
- `@typespec/compiler` >=1.0.0
|
|
153
|
-
- `@typespec/emitter-framework` ^0.17.0
|
|
154
|
-
|
|
155
|
-
### Dev
|
|
156
|
-
|
|
157
|
-
- `typescript` ^5.3.3
|
|
158
|
-
- `eslint` ^9.15.0
|
|
159
|
-
- `prettier` ^3.3.3
|
|
160
|
-
|
|
161
|
-
### Generated Rust (example/output-rust/Cargo.toml)
|
|
162
|
-
|
|
163
|
-
```toml
|
|
164
|
-
serde = { version = "1.0", features = ["derive"] }
|
|
165
|
-
thiserror = "2.0"
|
|
166
|
-
uuid = { version = "1.23", features = ["serde"] }
|
|
167
|
-
chrono = { version = "0.4", features = ["serde"] }
|
|
168
|
-
regex = "1.12"
|
|
169
|
-
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "derive"] }
|
|
170
|
-
axum = "0.8"
|
|
171
|
-
eyre = "0.6"
|
|
172
|
-
async-trait = "0.1"
|
|
173
|
-
tokio = { version = "1.50", features = ["full"] }
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
## Configuration Files
|
|
177
|
-
|
|
178
|
-
### tspconfig.yaml
|
|
179
|
-
|
|
180
|
-
```yaml
|
|
181
|
-
emit:
|
|
182
|
-
- "@typespec/openapi3"
|
|
183
|
-
- "typespec-rust-emitter"
|
|
184
|
-
options:
|
|
185
|
-
"typespec-rust-emitter":
|
|
186
|
-
emitter-output-dir: "{output-dir}/../output-rust/src/generated"
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### tsconfig.json
|
|
190
|
-
|
|
191
|
-
- Target: ES2022
|
|
192
|
-
- Module: NodeNext
|
|
193
|
-
- Strict: true
|
|
194
|
-
- Root/Out: `.` → `dist`
|
|
195
|
-
|
|
196
|
-
## Development Conventions
|
|
197
|
-
|
|
198
|
-
1. **TypeScript**: Strict mode, ES2022, NodeNext modules
|
|
199
|
-
2. **Testing**: Node.js built-in test runner (`node:test`)
|
|
200
|
-
3. **Formatting**: Prettier (yaml config)
|
|
201
|
-
4. **Linting**: ESLint + typescript-eslint
|
|
202
|
-
5. **Code Style**: Match existing patterns in `src/emitter.ts`
|
|
203
|
-
|
|
204
|
-
## Rules
|
|
205
|
-
|
|
206
|
-
1. Always run full cycle after changes: `build → test → compile → check-rust`
|
|
207
|
-
2. Add tests for new features in `test/hello.test.ts`
|
|
208
|
-
3. Generated Rust must compile (verify with `just check-rust`)
|
|
209
|
-
4. Reference `oas3-gen/` for advanced codegen patterns
|
|
210
|
-
5. No implicit any (strict TypeScript)
|
|
211
|
-
|
|
212
|
-
## Recent Changes
|
|
213
|
-
|
|
214
|
-
### 2026-03-27: v0.4.0 - Individual Parameters Instead of Request Structs
|
|
215
|
-
|
|
216
|
-
**Breaking Changes**:
|
|
217
|
-
|
|
218
|
-
- Server trait methods now accept individual parameters instead of request structs
|
|
219
|
-
- `async fn subjects_create(&self, claims: Self::Claims, request: SubjectsCreateRequest)` → `async fn subjects_create(&self, claims: Self::Claims, group_id: i64, body: CreateSubjectBody)`
|
|
220
|
-
- Request structs (`SubjectsCreateRequest`, etc.) are no longer generated
|
|
221
|
-
- Handler extractor order follows axum conventions: `State` → `Extension` → `Path` → `Json`
|
|
222
|
-
|
|
223
|
-
**Benefits**:
|
|
224
|
-
|
|
225
|
-
- Cleaner server trait API - no boilerplate request structs
|
|
226
|
-
- Direct parameter access in handler implementations
|
|
227
|
-
- Follows axum best practices for extractor ordering
|
|
228
|
-
- Better IDE autocomplete for handler parameters
|
|
229
|
-
|
|
230
|
-
**Example**:
|
|
231
|
-
|
|
232
|
-
```typespec
|
|
233
|
-
@post
|
|
234
|
-
@route("/groups/{groupId}/subjects")
|
|
235
|
-
op create(@path groupId: int64, @body body: CreateSubjectBody): Subject;
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
```rust
|
|
239
|
-
// Server trait
|
|
240
|
-
async fn subjects_create(&self, claims: Self::Claims, group_id: i64, body: CreateSubjectBody) -> Result<...>;
|
|
241
|
-
|
|
242
|
-
// Handler
|
|
243
|
-
Extension(claims): Extension<S::Claims>,
|
|
244
|
-
Path(group_id): Path<i64>,
|
|
245
|
-
Json(payload): Json<CreateSubjectBody>
|
|
246
|
-
let result = service.subjects_create(claims, group_id, payload).await;
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### 2026-03-27: v0.3.0 - Custom Attributes & Enum Derives
|
|
250
|
-
|
|
251
|
-
**New Features**:
|
|
252
|
-
|
|
253
|
-
- `@rustAttr` / `@rustAttrs` decorators for custom Rust attributes (models & enums)
|
|
254
|
-
- `@rustDerive` / `@rustDerives` now work on enums (previously models only)
|
|
255
|
-
- Attributes render after `#[derive(...)]` for proper Rust syntax
|
|
256
|
-
|
|
257
|
-
**Example**:
|
|
258
|
-
|
|
259
|
-
```typespec
|
|
260
|
-
@rustDerive("sqlx::Type")
|
|
261
|
-
@rustAttr("sqlx(type_name = \"study_status\")")
|
|
262
|
-
enum StudyStatus { Starting, Paused }
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
```rust
|
|
266
|
-
#[derive(..., sqlx::Type)]
|
|
267
|
-
#[sqlx(type_name = "study_status")]
|
|
268
|
-
pub enum StudyStatus { ... }
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### 2026-03-27: v0.2.0 - Server Trait Refactoring
|
|
272
|
-
|
|
273
|
-
**Changes**:
|
|
274
|
-
|
|
275
|
-
- `pub trait Server<Claims>` → `pub trait Server` with `type Claims` associated type
|
|
276
|
-
- Removed generated `Claims` struct from `types.rs` (users define their own)
|
|
277
|
-
- `create_router<S, M>(service: S, middleware: M)` now accepts middleware function
|
|
278
|
-
- Middleware wraps protected routes: `middleware(protected)`
|
|
279
|
-
- Single `.with_state(service)` call at end of router creation
|
|
280
|
-
|
|
281
|
-
**Benefits**:
|
|
282
|
-
|
|
283
|
-
- Cleaner API - Claims type defined by user, not generated
|
|
284
|
-
- Flexible middleware - any function `FnOnce(Router<S>) -> Router<S>`
|
|
285
|
-
- No duplicate `.with_state(service.clone())` calls
|
|
286
|
-
|
|
287
|
-
## TODO
|
|
288
|
-
|
|
289
|
-
- [ ] More server trait test coverage
|
|
290
|
-
- [ ] Integration tests with Rust compilation
|
|
291
|
-
- [ ] Expand scalar format mappings (more chrono types)
|
|
292
|
-
- [ ] Support additional HTTP decorators
|