typespec-rust-emitter 0.1.0 → 0.3.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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(just *)",
5
+ "Bash(npm run *)",
6
+ "Bash(npm test)",
7
+ "Bash(cargo check)"
8
+ ]
9
+ },
10
+ "$version": 3
11
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.0] - 2026-03-27
9
+
10
+ ### Added
11
+
12
+ - `@rustAttr` and `@rustAttrs` decorators for adding custom Rust attributes to models and enums
13
+ - Example: `@rustAttr("sqlx(type_name = \"user\")")` generates `#[sqlx(type_name = "user")]`
14
+ - Support for `@rustDerive` and `@rustDerives` on enums (previously only models)
15
+ - Enables sqlx and other derive macros on enum types
16
+
17
+ ### Changed
18
+
19
+ - Custom attributes are now rendered after `#[derive(...)]` for proper Rust syntax
20
+ - Updated example to demonstrate `@rustDerive` + `@rustAttr` combination on enums
21
+
22
+ ### Example
23
+
24
+ ```typespec
25
+ @rustDerive("sqlx::Type")
26
+ @rustAttr("sqlx(type_name = \"study_status\")")
27
+ enum StudyStatus {
28
+ Starting,
29
+ Paused,
30
+ }
31
+ ```
32
+
33
+ Generates:
34
+
35
+ ```rust
36
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, sqlx::Type)]
37
+ #[sqlx(type_name = "study_status")]
38
+ pub enum StudyStatus {
39
+ #[serde(rename = "Starting")]
40
+ Starting,
41
+ #[serde(rename = "Paused")]
42
+ Paused,
43
+ }
44
+ ```
45
+
46
+ ## [0.2.0] - 2026-03-27
47
+
48
+ ### Changed
49
+
50
+ - **Breaking**: Server trait now uses associated type instead of generic parameter
51
+ - `pub trait Server<Claims>` → `pub trait Server` with `type Claims: Send + Sync + 'static`
52
+ - Users must now define their own `Claims` type instead of using the generated one
53
+ - **Breaking**: `create_router` signature changed
54
+ - Now accepts middleware function: `create_router<S, M>(service: S, middleware: M)`
55
+ - Middleware type: `FnOnce(Router<S>) -> Router<S>`
56
+ - Protected routes wrapped with: `middleware(protected)`
57
+ - Router now applies `.with_state(service)` once at the end instead of per-route
58
+ - Removed generated `Claims` struct from `types.rs`
59
+
60
+ ### Added
61
+
62
+ - Demo server implementation in `example/output-rust/src/main.rs`
63
+ - Full server trait documentation in README.md
64
+
65
+ ### Benefits
66
+
67
+ - Cleaner API - Claims type defined by user, not generated
68
+ - Flexible middleware - any function matching the trait bound
69
+ - No duplicate `.with_state(service.clone())` calls
70
+ - Better separation of concerns - auth logic owned by user
71
+
72
+ ## [0.1.0] - 2026-03-27
73
+
74
+ ### Added
75
+
76
+ - Initial release
77
+ - TypeSpec to Rust code generation for:
78
+ - Models → `pub struct` with serde derives
79
+ - Enums → `pub enum` with serde rename
80
+ - Unions → `Option<T>` for nullable types
81
+ - Scalars → `pub type` or `pub struct` with pattern validation
82
+ - Error models → `thiserror::Error` derive
83
+ - Custom derives via `@rustDerive` and `@rustDerives` decorators
84
+ - Server trait generation for axum HTTP operations
85
+ - Support for `@useAuth` decorator to mark protected routes
86
+ - Type mappings for common TypeSpec types and formats
package/DEV.md ADDED
@@ -0,0 +1,81 @@
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 ADDED
@@ -0,0 +1,238 @@
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
+ ```bash
41
+ just build # Compile TypeScript (npm run build)
42
+ just test # Run unit tests (npm test)
43
+ just compile # Generate Rust code from example
44
+ just check-rust # Verify Rust compiles (cargo check)
45
+ ```
46
+
47
+ ### Individual Commands
48
+ ```bash
49
+ # Development
50
+ npm run build # tsc compile
51
+ npm run watch # Watch mode
52
+ npm run lint # ESLint check
53
+ npm run lint:fix # Auto-fix lint
54
+ npm run format # Prettier format
55
+ npm run format:check
56
+
57
+ # Full pipeline
58
+ just install # npm install (root + example)
59
+ just publish # build + npm publish
60
+ ```
61
+
62
+ ## Type Mappings (TypeSpec → Rust)
63
+
64
+ | TypeSpec | Rust |
65
+ |----------|------|
66
+ | `string` | `String` |
67
+ | `int8/16/32/64` | `i8/i16/i32/i64` |
68
+ | `uint8/16/32/64` | `u8/u16/u32/u64` |
69
+ | `float32/64` | `f32/f64` |
70
+ | `boolean` | `bool` |
71
+ | `bytes` | `Vec<u8>` |
72
+ | `T[]` | `Vec<T>` |
73
+ | `Record<T>` | `HashMap<String, T>` |
74
+ | `T \| null` | `Option<T>` |
75
+
76
+ ### Format Mappings
77
+ | `@format()` | Rust Type |
78
+ |-------------|-----------|
79
+ | `"uuid"` | `uuid::Uuid` |
80
+ | `"date"` | `chrono::NaiveDate` |
81
+ | `"time"` | `chrono::NaiveTime` |
82
+ | `"date-time"` | `chrono::DateTime<Utc>` |
83
+
84
+ ## Decorators
85
+
86
+ | Decorator | Effect |
87
+ |-----------|--------|
88
+ | `@error` | Adds `thiserror::Error` derive + `#[error("{code}: {message}")]` |
89
+ | `@pattern("regex")` | Generates `TryFrom<String>` with regex validation |
90
+ | `@rustDerive("...")` | Adds custom derive (models & enums, e.g., `sqlx::FromRow`, `sqlx::Type`) |
91
+ | `@rustDerives("...", "...")` | Adds multiple custom derives (models & enums) |
92
+ | `@rustAttr("...")` | Adds custom Rust attribute (models & enums, e.g., `sqlx(type_name = "...")`) |
93
+ | `@rustAttrs("...", "...")` | Adds multiple custom attributes (models & enums) |
94
+ | `@doc("...")` | Generates `///` doc comments |
95
+ | `@useAuth(BearerAuth)` | Marks operation as protected (adds `Claims` param) |
96
+ | `@maxLength(n)` | Documentation only |
97
+
98
+ ## Output Files
99
+
100
+ ### types.rs
101
+ - All models → `pub struct` with serde derives
102
+ - Enums → `pub enum` with serde rename
103
+ - Unions → `Option<T>` for nullable, or sum types
104
+ - Scalars → `pub type` or `pub struct` (if pattern validation)
105
+
106
+ ### server.rs
107
+ - `Server` trait with `type Claims` associated type
108
+ - Request structs per operation
109
+ - Response enums with status code variants
110
+ - `create_router<S, M>(service: S, middleware: M)` function with axum routes
111
+
112
+ ## Key Implementation Details
113
+
114
+ ### emitter.ts Architecture
115
+ 1. `navigateProgram()` - Walks TypeSpec AST
116
+ 2. Collects: models, enums, unions, scalars, operations
117
+ 3. Processes decorators (`@error`, `@pattern`, `@rustDerive`)
118
+ 4. Generates Rust via string templates
119
+ 5. Emits files to configured output directory
120
+
121
+ ### Server Trait Generation
122
+ - Generates `Server` trait with `type Claims: Send + Sync + 'static` associated type
123
+ - Handler functions: `pub async fn {op}_handler<S>(...)` with `<S>` generics only
124
+ - Protected routes (`@useAuth`) receive `claims: Self::Claims` via `Extension<Self::Claims>`
125
+ - Public routes don't require claims
126
+ - `create_router<S, M>(service: S, middleware: M)` accepts middleware function
127
+ - Middleware wraps protected routes: `middleware(protected)`
128
+ - Router merges public/protected, applies `.with_state(service)` once at end
129
+
130
+ ### Test Pattern
131
+ ```typescript
132
+ import { emit } from "./test-host.js";
133
+ const results = await emit(`model User { name: string; }`);
134
+ const output = results["types.rs"];
135
+ strictEqual(output.includes("pub struct User"), true);
136
+ ```
137
+
138
+ ## Dependencies
139
+
140
+ ### Peer (Required)
141
+ - `@typespec/compiler` >=1.0.0
142
+ - `@typespec/emitter-framework` ^0.17.0
143
+
144
+ ### Dev
145
+ - `typescript` ^5.3.3
146
+ - `eslint` ^9.15.0
147
+ - `prettier` ^3.3.3
148
+
149
+ ### Generated Rust (example/output-rust/Cargo.toml)
150
+ ```toml
151
+ serde = { version = "1.0", features = ["derive"] }
152
+ thiserror = "2.0"
153
+ uuid = { version = "1.23", features = ["serde"] }
154
+ chrono = { version = "0.4", features = ["serde"] }
155
+ regex = "1.12"
156
+ sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "derive"] }
157
+ axum = "0.8"
158
+ eyre = "0.6"
159
+ async-trait = "0.1"
160
+ tokio = { version = "1.50", features = ["full"] }
161
+ ```
162
+
163
+ ## Configuration Files
164
+
165
+ ### tspconfig.yaml
166
+ ```yaml
167
+ emit:
168
+ - "@typespec/openapi3"
169
+ - "typespec-rust-emitter"
170
+ options:
171
+ "typespec-rust-emitter":
172
+ emitter-output-dir: "{output-dir}/../output-rust/src/generated"
173
+ ```
174
+
175
+ ### tsconfig.json
176
+ - Target: ES2022
177
+ - Module: NodeNext
178
+ - Strict: true
179
+ - Root/Out: `.` → `dist`
180
+
181
+ ## Development Conventions
182
+
183
+ 1. **TypeScript**: Strict mode, ES2022, NodeNext modules
184
+ 2. **Testing**: Node.js built-in test runner (`node:test`)
185
+ 3. **Formatting**: Prettier (yaml config)
186
+ 4. **Linting**: ESLint + typescript-eslint
187
+ 5. **Code Style**: Match existing patterns in `src/emitter.ts`
188
+
189
+ ## Rules
190
+
191
+ 1. Always run full cycle after changes: `build → test → compile → check-rust`
192
+ 2. Add tests for new features in `test/hello.test.ts`
193
+ 3. Generated Rust must compile (verify with `just check-rust`)
194
+ 4. Reference `oas3-gen/` for advanced codegen patterns
195
+ 5. No implicit any (strict TypeScript)
196
+
197
+ ## Recent Changes
198
+
199
+ ### 2026-03-27: v0.3.0 - Custom Attributes & Enum Derives
200
+
201
+ **New Features**:
202
+ - `@rustAttr` / `@rustAttrs` decorators for custom Rust attributes (models & enums)
203
+ - `@rustDerive` / `@rustDerives` now work on enums (previously models only)
204
+ - Attributes render after `#[derive(...)]` for proper Rust syntax
205
+
206
+ **Example**:
207
+ ```typespec
208
+ @rustDerive("sqlx::Type")
209
+ @rustAttr("sqlx(type_name = \"study_status\")")
210
+ enum StudyStatus { Starting, Paused }
211
+ ```
212
+
213
+ ```rust
214
+ #[derive(..., sqlx::Type)]
215
+ #[sqlx(type_name = "study_status")]
216
+ pub enum StudyStatus { ... }
217
+ ```
218
+
219
+ ### 2026-03-27: v0.2.0 - Server Trait Refactoring
220
+
221
+ **Changes**:
222
+ - `pub trait Server<Claims>` → `pub trait Server` with `type Claims` associated type
223
+ - Removed generated `Claims` struct from `types.rs` (users define their own)
224
+ - `create_router<S, M>(service: S, middleware: M)` now accepts middleware function
225
+ - Middleware wraps protected routes: `middleware(protected)`
226
+ - Single `.with_state(service)` call at end of router creation
227
+
228
+ **Benefits**:
229
+ - Cleaner API - Claims type defined by user, not generated
230
+ - Flexible middleware - any function `FnOnce(Router<S>) -> Router<S>`
231
+ - No duplicate `.with_state(service.clone())` calls
232
+
233
+ ## TODO
234
+
235
+ - [ ] More server trait test coverage
236
+ - [ ] Integration tests with Rust compilation
237
+ - [ ] Expand scalar format mappings (more chrono types)
238
+ - [ ] Support additional HTTP decorators
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Authored by [opencode](https://opencode.ai)
1
+ Authored by [opencode](https://opencode.ai) & Qwen
2
2
 
3
3
  ---
4
4
 
@@ -18,7 +18,9 @@ A TypeSpec emitter that generates idiomatic Rust types and structs from TypeSpec
18
18
  - **Inheritance**: Supports model inheritance with `getAllProperties()`
19
19
  - **Error Models**: Generates `thiserror::Error` derive with `#[error(...)]` attributes
20
20
  - **Pattern Validation**: Supports `@pattern` decorators with `TryFrom<String>` validation
21
- - **Custom Derives**: Add arbitrary Rust derive macros via `@rustDerive` decorator
21
+ - **Custom Derives**: Add arbitrary Rust derive macros via `@rustDerive` decorator (models & enums)
22
+ - **Custom Attributes**: Add arbitrary Rust attributes via `@rustAttr` decorator (models & enums)
23
+ - **Server Trait**: Generates axum server trait and router from HTTP operations
22
24
 
23
25
  ## Installation
24
26
 
@@ -36,8 +38,6 @@ npm install @typespec/compiler @typespec/emitter-framework
36
38
 
37
39
  ## Usage
38
40
 
39
- ## Usage
40
-
41
41
  ### Basic Model
42
42
 
43
43
  ```typespec
@@ -179,6 +179,174 @@ pub struct GroupStatistics {
179
179
  }
180
180
  ```
181
181
 
182
+ ### Custom Attributes
183
+
184
+ ```typespec
185
+ import "typespec-emitter";
186
+
187
+ @rustAttr("sqlx(type_name = \"user\")")
188
+ model User {
189
+ name: string;
190
+ }
191
+
192
+ @rustDerive("sqlx::Type")
193
+ @rustAttr("sqlx(type_name = \"study_status\")")
194
+ enum StudyStatus {
195
+ Starting,
196
+ Paused,
197
+ }
198
+ ```
199
+
200
+ Generates:
201
+
202
+ ```rust
203
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
204
+ #[sqlx(type_name = "user")]
205
+ pub struct User {
206
+ #[serde(rename = "name")]
207
+ pub name: String,
208
+ }
209
+
210
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, sqlx::Type)]
211
+ #[sqlx(type_name = "study_status")]
212
+ pub enum StudyStatus {
213
+ #[serde(rename = "Starting")]
214
+ Starting,
215
+ #[serde(rename = "Paused")]
216
+ Paused,
217
+ }
218
+ ```
219
+
220
+ ## Server Trait Generation
221
+
222
+ The emitter can generate a complete axum server trait and router from TypeSpec HTTP operations.
223
+
224
+ ### Example TypeSpec
225
+
226
+ ```typespec
227
+ @route("/groups")
228
+ namespace Groups {
229
+ @get
230
+ @route("")
231
+ op list(): Group[];
232
+
233
+ @post
234
+ @route("")
235
+ @useAuth(BearerAuth)
236
+ op create(@body body: CreateGroupBody): Group;
237
+
238
+ @get
239
+ @route("/{id}")
240
+ op getById(@path id: int64): Group;
241
+ }
242
+ ```
243
+
244
+ ### Generated Server Trait
245
+
246
+ ```rust
247
+ #[async_trait]
248
+ pub trait Server: Send + Sync {
249
+ type Claims: Send + Sync + 'static;
250
+
251
+ async fn groups_list(&self, request: GroupsListRequest) -> Result<GroupsListResponse>;
252
+ async fn groups_create(&self, claims: Self::Claims, request: GroupsCreateRequest) -> Result<GroupsCreateResponse>;
253
+ async fn groups_get_by_id(&self, request: GroupsGetByIdRequest) -> Result<GroupsGetByIdResponse>;
254
+ }
255
+ ```
256
+
257
+ ### Implementing the Server
258
+
259
+ ```rust
260
+ #[derive(Clone)]
261
+ pub struct AppState {
262
+ pub db: Arc<SqlitePool>,
263
+ }
264
+
265
+ pub struct MyServer {
266
+ state: AppState,
267
+ }
268
+
269
+ #[async_trait::async_trait]
270
+ impl Server for MyServer {
271
+ type Claims = Claims; // Your custom claims type
272
+
273
+ async fn groups_list(
274
+ &self,
275
+ _request: GroupsListRequest,
276
+ ) -> eyre::Result<GroupsListResponse> {
277
+ let groups = sqlx::query_as::<_, Group>("SELECT * FROM groups")
278
+ .fetch_all(&self.state.db)
279
+ .await?;
280
+ Ok(GroupsListResponse::Ok(Json(groups)))
281
+ }
282
+
283
+ async fn groups_create(
284
+ &self,
285
+ claims: Self::Claims,
286
+ request: GroupsCreateRequest,
287
+ ) -> eyre::Result<GroupsCreateResponse> {
288
+ // claims.sub contains the authenticated user ID
289
+ let group = sqlx::query_as::<_, Group>(
290
+ "INSERT INTO groups (name, owner_id) VALUES ($1, $2) RETURNING *"
291
+ )
292
+ .bind(&request.body.name)
293
+ .bind(&claims.sub)
294
+ .fetch_one(&self.state.db)
295
+ .await?;
296
+ Ok(GroupsCreateResponse::Created(Json(group)))
297
+ }
298
+
299
+ // ... implement other methods
300
+ }
301
+ ```
302
+
303
+ ### Creating the Router
304
+
305
+ ```rust
306
+ // Define your claims type
307
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
308
+ pub struct Claims {
309
+ pub sub: String,
310
+ pub exp: usize,
311
+ }
312
+
313
+ // Create auth middleware
314
+ async fn auth_middleware(
315
+ Extension(claims): Extension<Claims>,
316
+ request: Request<axum::body::Body>,
317
+ next: Next,
318
+ ) -> Result<Response, StatusCode> {
319
+ // Validate claims (e.g., check expiration)
320
+ if claims.exp < (chrono::Utc::now().timestamp() as usize) {
321
+ return Err(StatusCode::UNAUTHORIZED);
322
+ }
323
+ Ok(next.run(request).await)
324
+ }
325
+
326
+ // Apply middleware to protected routes
327
+ fn with_auth<S>(router: Router<S>) -> Router<S>
328
+ where
329
+ S: Server + Clone + Send + Sync + 'static,
330
+ S::Claims: Send + Sync + Clone + 'static,
331
+ {
332
+ router.layer(axum::middleware::from_fn(auth_middleware))
333
+ }
334
+
335
+ // Create the router
336
+ let server = MyServer::new(state);
337
+ let router = create_router(server, with_auth);
338
+ axum::serve(listener, router).await?;
339
+ ```
340
+
341
+ ### Protected Routes
342
+
343
+ Operations decorated with `@useAuth` will:
344
+
345
+ - Receive `claims: Self::Claims` as the first parameter
346
+ - Be wrapped with the middleware passed to `create_router`
347
+
348
+ Public routes do not receive claims and are not wrapped with auth middleware.
349
+
182
350
  ## Type Mappings
183
351
 
184
352
  | TypeSpec Type | Rust Type |
@@ -193,6 +361,28 @@ pub struct GroupStatistics {
193
361
  | `Record<string>` | `std::collections::HashMap<String, String>` |
194
362
  | `T \| null` | `Option<T>` |
195
363
 
364
+ ### Format Mappings
365
+
366
+ | `@format()` | Rust Type |
367
+ | ------------- | ----------------------- |
368
+ | `"uuid"` | `uuid::Uuid` |
369
+ | `"date"` | `chrono::NaiveDate` |
370
+ | `"time"` | `chrono::NaiveTime` |
371
+ | `"date-time"` | `chrono::DateTime<Utc>` |
372
+
373
+ ## Decorators
374
+
375
+ | Decorator | Description |
376
+ | ---------------------------- | ---------------------------------------------------------------- |
377
+ | `@error` | Adds `thiserror::Error` derive with error message |
378
+ | `@pattern("regex")` | Generates `TryFrom<String>` with regex validation |
379
+ | `@rustDerive("...")` | Adds a custom derive macro (models & enums) |
380
+ | `@rustDerives("...", "...")` | Adds multiple custom derive macros (models & enums) |
381
+ | `@rustAttr("...")` | Adds a custom Rust attribute (models & enums) |
382
+ | `@rustAttrs("...", "...")` | Adds multiple custom Rust attributes (models & enums) |
383
+ | `@doc("...")` | Generates `///` doc comments |
384
+ | `@useAuth(AuthType)` | Marks operation as protected, adds `Claims` parameter |
385
+
196
386
  ## Building
197
387
 
198
388
  ```bash
@@ -4,4 +4,6 @@ export interface RustEmitterOptions {
4
4
  }
5
5
  export declare function $rustDerive(context: DecoratorContext, target: Type, derive: string): void;
6
6
  export declare function $rustDerives(context: DecoratorContext, target: Type, ...derives: string[]): void;
7
+ export declare function $rustAttr(context: DecoratorContext, target: Type, attr: string): void;
8
+ export declare function $rustAttrs(context: DecoratorContext, target: Type, ...attrs: string[]): void;
7
9
  export declare function $onEmit(context: EmitContext<RustEmitterOptions>, _options?: RustEmitterOptions): Promise<void>;