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.
Files changed (43) hide show
  1. package/README.md +220 -0
  2. package/dist/src/emitter.d.ts +7 -0
  3. package/dist/src/emitter.js +490 -0
  4. package/dist/src/emitter.js.map +1 -0
  5. package/dist/src/index.d.ts +3 -0
  6. package/dist/src/index.js +4 -0
  7. package/dist/src/index.js.map +1 -0
  8. package/dist/src/lib.d.ts +12 -0
  9. package/dist/src/lib.js +7 -0
  10. package/dist/src/lib.js.map +1 -0
  11. package/dist/src/testing/index.d.ts +2 -0
  12. package/dist/src/testing/index.js +8 -0
  13. package/dist/src/testing/index.js.map +1 -0
  14. package/dist/test/hello.test.d.ts +1 -0
  15. package/dist/test/hello.test.js +140 -0
  16. package/dist/test/hello.test.js.map +1 -0
  17. package/dist/test/test-host.d.ts +4 -0
  18. package/dist/test/test-host.js +16 -0
  19. package/dist/test/test-host.js.map +1 -0
  20. package/eslint.config.js +20 -0
  21. package/example/lib/learning/models.tsp +189 -0
  22. package/example/lib/learning/operations.tsp +319 -0
  23. package/example/main.tsp +8 -0
  24. package/example/output-rust/Cargo.lock +1731 -0
  25. package/example/output-rust/Cargo.toml +12 -0
  26. package/example/output-rust/src/generated/mod.rs +1 -0
  27. package/example/output-rust/src/generated/types.rs +315 -0
  28. package/example/output-rust/src/main.rs +5 -0
  29. package/example/output-rust/src/mod.rs +1 -0
  30. package/example/package-lock.json +1495 -0
  31. package/example/package.json +15 -0
  32. package/example/tspconfig.yaml +10 -0
  33. package/justfile +15 -0
  34. package/package.json +64 -0
  35. package/prettierrc.yaml +8 -0
  36. package/src/emitter.ts +685 -0
  37. package/src/index.ts +3 -0
  38. package/src/lib.ts +8 -0
  39. package/src/lib.tsp +6 -0
  40. package/src/testing/index.ts +8 -0
  41. package/test/hello.test.ts +168 -0
  42. package/test/test-host.ts +20 -0
  43. 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
+ [![npm version](https://img.shields.io/npm/v/typespec-rust-emitter.svg)](https://www.npmjs.com/package/typespec-rust-emitter)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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>;