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
@@ -0,0 +1,168 @@
1
+ import { strictEqual } from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { emit } from "./test-host.js";
4
+
5
+ describe("Rust emitter", () => {
6
+ it("emits basic model", async () => {
7
+ const results = await emit(`
8
+ model User {
9
+ name: string;
10
+ age: int32;
11
+ }
12
+ `);
13
+ const output = results["types.rs"];
14
+ strictEqual(output.includes("pub struct User"), true);
15
+ strictEqual(output.includes("pub name: String,"), true);
16
+ strictEqual(output.includes("pub age: i32,"), true);
17
+ });
18
+
19
+ it("emits optional properties", async () => {
20
+ const results = await emit(`
21
+ model Person {
22
+ firstName: string;
23
+ middleName?: string;
24
+ lastName: string;
25
+ }
26
+ `);
27
+ const output = results["types.rs"];
28
+ strictEqual(output.includes('#[serde(rename = "firstName")]'), true);
29
+ strictEqual(output.includes("pub first_name: String,"), true);
30
+ strictEqual(output.includes('#[serde(rename = "middleName")]'), true);
31
+ strictEqual(output.includes("pub middle_name: Option<String>,"), true);
32
+ strictEqual(output.includes("#[serde(skip_serializing_if"), true);
33
+ strictEqual(output.includes('#[serde(rename = "lastName")]'), true);
34
+ strictEqual(output.includes("pub last_name: String,"), true);
35
+ });
36
+
37
+ it("emits string enum", async () => {
38
+ const results = await emit(`
39
+ enum Status {
40
+ active,
41
+ inactive,
42
+ pending,
43
+ }
44
+ `);
45
+ const output = results["types.rs"];
46
+ strictEqual(output.includes("pub enum Status"), true);
47
+ strictEqual(output.includes('#[serde(rename = "active")]'), true);
48
+ strictEqual(output.includes("Active,"), true);
49
+ strictEqual(output.includes("Inactive,"), true);
50
+ strictEqual(output.includes("Pending,"), true);
51
+ });
52
+
53
+ it("emits integer enum", async () => {
54
+ const results = await emit(`
55
+ enum Priority {
56
+ low: 1,
57
+ medium: 2,
58
+ high: 3,
59
+ }
60
+ `);
61
+ const output = results["types.rs"];
62
+ strictEqual(output.includes("pub enum Priority"), true);
63
+ strictEqual(output.includes("Low = 1,"), true);
64
+ strictEqual(output.includes("Medium = 2,"), true);
65
+ strictEqual(output.includes("High = 3,"), true);
66
+ });
67
+
68
+ it("emits nested models", async () => {
69
+ const results = await emit(`
70
+ model Address {
71
+ street: string;
72
+ city: string;
73
+ }
74
+
75
+ model User {
76
+ name: string;
77
+ address: Address;
78
+ }
79
+ `);
80
+ const output = results["types.rs"];
81
+ strictEqual(output.includes("pub struct Address"), true);
82
+ strictEqual(output.includes("pub struct User"), true);
83
+ strictEqual(output.includes("pub address: Address,"), true);
84
+ });
85
+
86
+ it("includes serde derive macros", async () => {
87
+ const results = await emit(`
88
+ model Test {
89
+ value: string;
90
+ }
91
+ `);
92
+ const output = results["types.rs"];
93
+ strictEqual(
94
+ output.includes(
95
+ "#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]",
96
+ ),
97
+ true,
98
+ );
99
+ });
100
+
101
+ it("emits arrays as Vec", async () => {
102
+ const results = await emit(`
103
+ model Test {
104
+ items: string[];
105
+ }
106
+ `);
107
+ const output = results["types.rs"];
108
+ strictEqual(output.includes("pub items: Vec<String>,"), true);
109
+ });
110
+
111
+ it("emits maps as HashMap", async () => {
112
+ const results = await emit(`
113
+ model Test {
114
+ metadata: Record<string>;
115
+ }
116
+ `);
117
+ const output = results["types.rs"];
118
+ strictEqual(
119
+ output.includes("std::collections::HashMap<String, String>"),
120
+ true,
121
+ );
122
+ });
123
+
124
+ it("emits nullable types as Option", async () => {
125
+ const results = await emit(`
126
+ model Test {
127
+ deletedAt: string | null;
128
+ }
129
+ `);
130
+ const output = results["types.rs"];
131
+ strictEqual(output.includes('#[serde(rename = "deletedAt")]'), true);
132
+ strictEqual(output.includes("pub deleted_at: Option<String>,"), true);
133
+ });
134
+
135
+ it("emits custom rustDerive macros", async () => {
136
+ const results = await emit(`
137
+ import "typespec-rust-emitter";
138
+
139
+ @rustDerive("sqlx::FromRow")
140
+ model User {
141
+ name: string;
142
+ age: int32;
143
+ }
144
+ `);
145
+ const output = results["types.rs"];
146
+ strictEqual(
147
+ output.includes(
148
+ "#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow)]",
149
+ ),
150
+ true,
151
+ );
152
+ });
153
+
154
+ it("emits multiple rustDerive macros via $rustDerives", async () => {
155
+ const results = await emit(`
156
+ model Statistics {
157
+ count: int32;
158
+ }
159
+ `);
160
+ const output = results["types.rs"];
161
+ strictEqual(
162
+ output.includes(
163
+ "#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]",
164
+ ),
165
+ true,
166
+ );
167
+ });
168
+ });
@@ -0,0 +1,20 @@
1
+ import { Diagnostic, resolvePath } from "@typespec/compiler";
2
+ import { expectDiagnosticEmpty } from "@typespec/compiler/testing";
3
+ import { createTester } from "@typespec/compiler/testing";
4
+
5
+ export const Tester = createTester(resolvePath(import.meta.dirname, "../.."), {
6
+ libraries: ["typespec-rust-emitter"],
7
+ }).emit("typespec-rust-emitter");
8
+
9
+ export async function emitWithDiagnostics(
10
+ code: string,
11
+ ): Promise<[Record<string, string>, readonly Diagnostic[]]> {
12
+ const [{ outputs }, diagnostics] = await Tester.compileAndDiagnose(code);
13
+ return [outputs, diagnostics];
14
+ }
15
+
16
+ export async function emit(code: string): Promise<Record<string, string>> {
17
+ const [result, diagnostics] = await emitWithDiagnostics(code);
18
+ expectDiagnosticEmpty(diagnostics);
19
+ return result;
20
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "lib": ["ES2022"],
8
+ "rootDir": ".",
9
+ "outDir": "dist",
10
+ "sourceMap": true,
11
+ "declaration": true,
12
+ "skipLibCheck": true,
13
+
14
+ /* Linting */
15
+ "strict": true
16
+ },
17
+ "include": ["src", "test"]
18
+ }