typespec-rust-emitter 0.7.0 → 0.9.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.
@@ -179,7 +179,7 @@ describe("Rust emitter", () => {
179
179
  const output = results["types.rs"];
180
180
  strictEqual(
181
181
  output.includes(
182
- "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, strum::Display)]",
182
+ "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize, strum::Display)]",
183
183
  ),
184
184
  true,
185
185
  );
@@ -198,7 +198,7 @@ describe("Rust emitter", () => {
198
198
  const output = results["types.rs"];
199
199
  strictEqual(
200
200
  output.includes(
201
- "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, strum::Display, strum::EnumString)]",
201
+ "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize, strum::Display, strum::EnumString)]",
202
202
  ),
203
203
  true,
204
204
  );
package/DEV.md DELETED
@@ -1,81 +0,0 @@
1
- # TypeSpec Rust Emitter - LLM Context
2
-
3
- ## Purpose
4
-
5
- TypeSpec emitter that generates idiomatic Rust code (structs, enums, server traits) from TypeSpec specifications.
6
-
7
- ## Key Paths
8
-
9
- | Path | Purpose |
10
- | ------------------------------------ | ------------------------------------------------------- |
11
- | `src/emitter.ts` | Main emitter logic (1464 lines) - TypeSpec→Rust codegen |
12
- | `src/lib.tsp` | Decorator declarations (`@rustDerive`, `@rustDerives`) |
13
- | `test/hello.test.ts` | Unit tests using `emit()` helper |
14
- | `example/lib/learning/` | Demo TypeSpec models & operations |
15
- | `example/output-rust/src/generated/` | Generated Rust output |
16
- | `oas3-gen/crates/oas3-gen/` | Reference template for codegen patterns |
17
-
18
- ## Commands (Run After Every Change)
19
-
20
- ```bash
21
- just build # npm run build - Compile TypeScript
22
- just test # npm test - Run unit tests
23
- just compile # tsp compile example → generates Rust code
24
- just check-rust # cargo check - Verify Rust compiles
25
- ```
26
-
27
- ## Type Mappings
28
-
29
- | TypeSpec | Rust |
30
- | ---------------------- | ----------------------- |
31
- | `string` | `String` |
32
- | `int32/64` | `i32/i64` |
33
- | `T \| null` | `Option<T>` |
34
- | `T[]` | `Vec<T>` |
35
- | `Record<T>` | `HashMap<String, T>` |
36
- | `@format("uuid")` | `uuid::Uuid` |
37
- | `@format("date-time")` | `chrono::DateTime<Utc>` |
38
-
39
- ## Architecture
40
-
41
- 1. `navigateProgram()` walks TypeSpec AST
42
- 2. Collects models, enums, operations
43
- 3. Processes decorators
44
- 4. Generates Rust code via string templates
45
- 5. Emits to `example/output-rust/src/generated/`
46
-
47
- ## Server Trait Generation
48
-
49
- - Generates `Server<Claims>` trait with async methods per operation
50
- - Handler functions: `pub async fn {op}_handler<S, Claims>(...)`
51
- - All handlers use `<S, Claims>` generics (even public routes)
52
- - Protected routes (`@useAuth`) receive `claims: Claims` parameter
53
- - Router splits public/protected routes, merges at end
54
-
55
- ## Test Pattern
56
-
57
- ```typescript
58
- import { emit } from "./test-host.js";
59
- const results = await emit(`model User { name: string; }`);
60
- const output = results["types.rs"];
61
- strictEqual(output.includes("pub struct User"), true);
62
- ```
63
-
64
- ## Recent Fix (2026-03-27)
65
-
66
- All handler functions now use `<S, Claims>` generics uniformly. Public routes include `Claims: Send + Sync + Clone + 'static` bound even without using Claims, because Server trait requires it.
67
-
68
- ## Rules
69
-
70
- 1. Always run full cycle: `build → test → compile → check-rust`
71
- 2. TypeScript strict mode enabled - no implicit any
72
- 3. Match existing code patterns in `src/emitter.ts`
73
- 4. Add tests for new features
74
- 5. Generated Rust must compile
75
-
76
- ## Todo
77
-
78
- - [ ] More server trait test coverage
79
- - [ ] Integration tests with Rust compilation
80
- - [ ] Expand scalar format mappings
81
- - [ ] Full working Rust server example
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