typespec-rust-emitter 0.12.0 → 0.13.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 (138) hide show
  1. package/AGENTS.md +82 -80
  2. package/CHANGELOG.md +17 -0
  3. package/dist/src/decorators/cache_control.d.ts +6 -0
  4. package/dist/src/decorators/cache_control.js +9 -0
  5. package/dist/src/decorators/cache_control.js.map +1 -0
  6. package/dist/src/decorators/etag_cache.d.ts +6 -0
  7. package/dist/src/decorators/etag_cache.js +9 -0
  8. package/dist/src/decorators/etag_cache.js.map +1 -0
  9. package/dist/src/decorators/index.d.ts +6 -0
  10. package/dist/src/decorators/index.js +7 -0
  11. package/dist/src/decorators/index.js.map +1 -0
  12. package/dist/src/decorators/rust_attr.d.ts +3 -0
  13. package/dist/src/decorators/rust_attr.js +45 -0
  14. package/dist/src/decorators/rust_attr.js.map +1 -0
  15. package/dist/src/decorators/rust_derive.d.ts +3 -0
  16. package/dist/src/decorators/rust_derive.js +39 -0
  17. package/dist/src/decorators/rust_derive.js.map +1 -0
  18. package/dist/src/decorators/rust_impl.d.ts +2 -0
  19. package/dist/src/decorators/rust_impl.js +19 -0
  20. package/dist/src/decorators/rust_impl.js.map +1 -0
  21. package/dist/src/decorators/rust_self.d.ts +3 -0
  22. package/dist/src/decorators/rust_self.js +35 -0
  23. package/dist/src/decorators/rust_self.js.map +1 -0
  24. package/dist/src/emitter.d.ts +2 -11
  25. package/dist/src/emitter.js +7 -1282
  26. package/dist/src/emitter.js.map +1 -1
  27. package/dist/src/formatter/index.d.ts +2 -0
  28. package/dist/src/formatter/index.js +3 -0
  29. package/dist/src/formatter/index.js.map +1 -0
  30. package/dist/src/formatter/mappings.d.ts +4 -0
  31. package/dist/src/formatter/mappings.js +68 -0
  32. package/dist/src/formatter/mappings.js.map +1 -0
  33. package/dist/src/formatter/strings.d.ts +4 -0
  34. package/dist/src/formatter/strings.js +32 -0
  35. package/dist/src/formatter/strings.js.map +1 -0
  36. package/dist/src/generator/etag_router.d.ts +30 -0
  37. package/dist/src/generator/etag_router.js +123 -0
  38. package/dist/src/generator/etag_router.js.map +1 -0
  39. package/dist/src/generator/index.d.ts +5 -0
  40. package/dist/src/generator/index.js +6 -0
  41. package/dist/src/generator/index.js.map +1 -0
  42. package/dist/src/generator/response_enums.d.ts +6 -0
  43. package/dist/src/generator/response_enums.js +58 -0
  44. package/dist/src/generator/response_enums.js.map +1 -0
  45. package/dist/src/generator/router.d.ts +7 -0
  46. package/dist/src/generator/router.js +227 -0
  47. package/dist/src/generator/router.js.map +1 -0
  48. package/dist/src/generator/server_trait.d.ts +6 -0
  49. package/dist/src/generator/server_trait.js +97 -0
  50. package/dist/src/generator/server_trait.js.map +1 -0
  51. package/dist/src/generator/types_file.d.ts +11 -0
  52. package/dist/src/generator/types_file.js +209 -0
  53. package/dist/src/generator/types_file.js.map +1 -0
  54. package/dist/src/index.d.ts +1 -1
  55. package/dist/src/index.js +1 -1
  56. package/dist/src/index.js.map +1 -1
  57. package/dist/src/lib.js +1 -1
  58. package/dist/src/lib.js.map +1 -1
  59. package/dist/src/models/index.d.ts +2 -0
  60. package/dist/src/models/index.js +3 -0
  61. package/dist/src/models/index.js.map +1 -0
  62. package/dist/src/models/keys.d.ts +6 -0
  63. package/dist/src/models/keys.js +8 -0
  64. package/dist/src/models/keys.js.map +1 -0
  65. package/dist/src/models/types.d.ts +45 -0
  66. package/dist/src/models/types.js +2 -0
  67. package/dist/src/models/types.js.map +1 -0
  68. package/dist/src/parser/decorators.d.ts +18 -0
  69. package/dist/src/parser/decorators.js +28 -0
  70. package/dist/src/parser/decorators.js.map +1 -0
  71. package/dist/src/parser/index.d.ts +6 -0
  72. package/dist/src/parser/index.js +7 -0
  73. package/dist/src/parser/index.js.map +1 -0
  74. package/dist/src/parser/operations.d.ts +13 -0
  75. package/dist/src/parser/operations.js +127 -0
  76. package/dist/src/parser/operations.js.map +1 -0
  77. package/dist/src/parser/parameters.d.ts +5 -0
  78. package/dist/src/parser/parameters.js +98 -0
  79. package/dist/src/parser/parameters.js.map +1 -0
  80. package/dist/src/parser/responses.d.ts +13 -0
  81. package/dist/src/parser/responses.js +132 -0
  82. package/dist/src/parser/responses.js.map +1 -0
  83. package/dist/src/parser/routes.d.ts +4 -0
  84. package/dist/src/parser/routes.js +36 -0
  85. package/dist/src/parser/routes.js.map +1 -0
  86. package/dist/src/parser/types.d.ts +9 -0
  87. package/dist/src/parser/types.js +157 -0
  88. package/dist/src/parser/types.js.map +1 -0
  89. package/dist/test/etag_cache.test.d.ts +1 -0
  90. package/dist/test/etag_cache.test.js +62 -0
  91. package/dist/test/etag_cache.test.js.map +1 -0
  92. package/dist/test/test-host.d.ts +11 -0
  93. package/dist/test/test-host.js +28 -0
  94. package/dist/test/test-host.js.map +1 -1
  95. package/example/main.tsp +27 -1
  96. package/example/output-rust/Cargo.lock +48 -0
  97. package/example/output-rust/Cargo.toml +1 -0
  98. package/example/output-rust/src/generated/server.rs +122 -11
  99. package/example/output-rust/src/generated/types.rs +6 -0
  100. package/example/output-rust/src/main.rs +60 -27
  101. package/justfile +31 -2
  102. package/package.json +1 -1
  103. package/scripts/update-golden.js +36 -0
  104. package/src/decorators/cache_control.ts +14 -0
  105. package/src/decorators/etag_cache.ts +14 -0
  106. package/src/decorators/index.ts +6 -0
  107. package/src/decorators/rust_attr.ts +61 -0
  108. package/src/decorators/rust_derive.ts +55 -0
  109. package/src/decorators/rust_impl.ts +29 -0
  110. package/src/decorators/rust_self.ts +42 -0
  111. package/src/emitter.ts +18 -1654
  112. package/src/formatter/index.ts +2 -0
  113. package/src/formatter/mappings.ts +70 -0
  114. package/src/formatter/strings.ts +33 -0
  115. package/src/generator/etag_router.ts +147 -0
  116. package/src/generator/index.ts +5 -0
  117. package/src/generator/response_enums.ts +76 -0
  118. package/src/generator/router.ts +280 -0
  119. package/src/generator/server_trait.ts +134 -0
  120. package/src/generator/types_file.ts +297 -0
  121. package/src/index.ts +3 -1
  122. package/src/lib.ts +1 -1
  123. package/src/lib.tsp +3 -1
  124. package/src/models/index.ts +2 -0
  125. package/src/models/keys.ts +7 -0
  126. package/src/models/types.ts +54 -0
  127. package/src/parser/decorators.ts +34 -0
  128. package/src/parser/index.ts +6 -0
  129. package/src/parser/operations.ts +158 -0
  130. package/src/parser/parameters.ts +117 -0
  131. package/src/parser/responses.ts +170 -0
  132. package/src/parser/routes.ts +47 -0
  133. package/src/parser/types.ts +215 -0
  134. package/test/etag_cache.test.ts +69 -0
  135. package/test/golden/etag_cache/server.rs +109 -0
  136. package/test/golden/etag_cache/spec.tsp +20 -0
  137. package/test/golden/etag_cache/types.rs +13 -0
  138. package/test/test-host.ts +43 -0
package/AGENTS.md CHANGED
@@ -1,120 +1,122 @@
1
- # TypeSpec Rust Emitter
1
+ # TypeSpec Rust Emitter: Project Governance & Guidelines
2
2
 
3
- TypeSpec emitter generating idiomatic Rust code (structs, enums, server traits).
3
+ This document serves as the authoritative guide for all contributors (human and AI) to the TypeSpec Rust Emitter project. It defines the architectural philosophy, development workflows, and quality standards required to maintain a high-quality, modular, and reliable emitter.
4
4
 
5
- ## Behavioral Guidelines
5
+ ---
6
6
 
7
- **Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
7
+ ## 1. Architecture Overview
8
8
 
9
- ### 1. Think Before Coding
9
+ The emitter is built on a **Modular Pipeline** architecture. It transforms TypeSpec models and operations into idiomatic Rust code through a series of decoupled stages.
10
10
 
11
- **Don't assume. Don't hide confusion. Surface tradeoffs.**
11
+ ### Core Modules
12
12
 
13
- Before implementing:
13
+ | Module | Responsibility |
14
+ | :-------------------- | :---------------------------------------------------------------------------------------------------------------------- |
15
+ | **`src/parser/`** | The "Discovery" layer. Traverses the TypeSpec AST to extract structured data (routes, parameters, responses, types). |
16
+ | **`src/generator/`** | The "Writer" layer. Takes parsed data and produces Rust source code. No TypeSpec-specific traversal should happen here. |
17
+ | **`src/decorators/`** | Implementation of TypeSpec `extern dec` functions. They attach metadata (via Symbols) used by the parser. |
18
+ | **`src/models/`** | Shared TypeScript interfaces and constant keys (Symbols) used for metadata storage. |
19
+ | **`src/formatter/`** | Naming convention logic (PascalCase to snake_case) and Rust identifier sanitization. |
14
20
 
15
- - State your assumptions explicitly. If uncertain, ask.
16
- - If multiple interpretations exist, present them - don't pick silently.
17
- - If a simpler approach exists, say so. Push back when warranted.
18
- - If something is unclear, stop. Name what's confusing. Ask.
21
+ ### Data Flow
19
22
 
20
- ### 2. Simplicity First
23
+ 1. **Emit Stage**: TypeSpec compiler calls `$onEmit`.
24
+ 2. **Parsing Stage**: The `Parser` traverses the program, identifying namespaces, models, and operations. Metadata from decorators is retrieved.
25
+ 3. **Generation Stage**: The `Generator` receives parsed metadata and emits strings for `types.rs` and `server.rs`.
26
+ 4. **Output Stage**: Files are written to the output directory.
21
27
 
22
- **Minimum code that solves the problem. Nothing speculative.**
28
+ ---
23
29
 
24
- - No features beyond what was asked.
25
- - No abstractions for single-use code.
26
- - No "flexibility" or "configurability" that wasn't requested.
27
- - No error handling for impossible scenarios.
28
- - If you write 200 lines and it could be 50, rewrite it.
30
+ ## 2. Onboarding & Workflow
29
31
 
30
- Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
32
+ ### Prerequisites
31
33
 
32
- ### 3. Surgical Changes
34
+ - **Node.js**: v18+
35
+ - **Rust**: Latest stable/nightly (for example validation).
36
+ - **Just**: Command runner (`just`).
37
+ - **TypeSpec**: `@typespec/compiler`.
33
38
 
34
- **Touch only what you must. Clean up only your own mess.**
39
+ ### Common Commands
35
40
 
36
- When editing existing code:
41
+ ```bash
42
+ # Build the TypeScript project
43
+ npm run build
37
44
 
38
- - Don't "improve" adjacent code, comments, or formatting.
39
- - Don't refactor things that aren't broken.
40
- - Match existing style, even if you'd do it differently.
41
- - If you notice unrelated dead code, mention it - don't delete it.
45
+ # Run TypeScript tests (including Golden File assertions)
46
+ just test
42
47
 
43
- When your changes create orphans:
48
+ # Re-emit example code and run cargo check/clippy on it
49
+ just check-rust
44
50
 
45
- - Remove imports/variables/functions that YOUR changes made unused.
46
- - Don't remove pre-existing dead code unless asked.
51
+ # Run EVERYTHING (Use this before any PR)
52
+ just check-all
47
53
 
48
- The test: Every changed line should trace directly to the user's request.
54
+ # Update golden files for a specific feature
55
+ just update-golden <feature_name>
56
+ ```
49
57
 
50
- ### 4. Goal-Driven Execution
58
+ ---
51
59
 
52
- **Define success criteria. Loop until verified.**
60
+ ## 3. Feature Contribution Pipeline
53
61
 
54
- Transform tasks into verifiable goals:
62
+ Every new feature must follow the **Standard Feature Lifecycle**:
55
63
 
56
- - "Add validation" "Write tests for invalid inputs, then make them pass"
57
- - "Fix the bug" "Write a test that reproduces it, then make it pass"
58
- - "Refactor X" "Ensure tests pass before and after"
64
+ 1. **Define TypeSpec API**: Add necessary decorators or models to `src/lib.tsp`.
65
+ 2. **Implement Decorator**: Add a handler in `src/decorators/` and export it via `src/index.ts`.
66
+ 3. **Update Parser**: Enhance `src/parser/` to detect and extract the new metadata.
67
+ 4. **Implement Generator**: Add logic in `src/generator/` to emit the corresponding Rust constructs (e.g., traits, attributes, or struct fields).
68
+ 5. **Create Test & Example**:
69
+ - Add a unit test in `test/` using the `emit` helper.
70
+ - Update `example/main.tsp` to demonstrate the feature in a real-world context.
59
71
 
60
- For multi-step tasks, state a brief plan:
72
+ ---
61
73
 
62
- ```
63
- 1. [Step] → verify: [check]
64
- 2. [Step] → verify: [check]
65
- 3. [Step] → verify: [check]
66
- ```
74
+ ## 4. Testing & Validation Protocol
67
75
 
68
- Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
76
+ Quality is enforced through a strict multi-layered validation system.
69
77
 
70
- ## Commands
78
+ ### Mandatory Test Structure
71
79
 
72
- ```bash
73
- # Build -> test (lint runs automatically)
74
- npm run build && npm test
80
+ - **1 Dedicated Test**: Every feature must have exactly one test file in `test/` (e.g., `test/etag_cache.test.ts`).
81
+ - **1 Functional Example**: The feature must be used in `example/main.tsp`.
75
82
 
76
- # Lint & format (run after every change)
77
- npm run lint
78
- npm run format
83
+ ### Golden File Testing
79
84
 
80
- # Verify Rust compiles
81
- just check-rust
82
- ```
85
+ We use "Golden Files" to ensure deterministic output.
86
+
87
+ - Expected outputs are stored in `test/golden/<feature>/`.
88
+ - Use `compareWithGolden(emitted, "feature", "file.rs")` in your tests.
89
+ - If output changes are intentional, use `just update-golden <feature>` to refresh the baseline.
90
+
91
+ ### Rust Integrity Check
83
92
 
84
- ## Project Layout
93
+ All code generated in the `example/` directory must be valid Rust.
85
94
 
86
- | Path | Purpose |
87
- | ---------------------- | -------------------------- |
88
- | `src/emitter.ts` | Main codegen (~1600 lines) |
89
- | `src/lib.tsp` | Decorator declarations |
90
- | `src/index.ts` | Exports |
91
- | `test/hello.test.ts` | Unit tests |
92
- | `example/lib/` | Demo TypeSpec specs |
93
- | `example/output-rust/` | Generated Rust |
95
+ - **`just check-rust`** automatically runs `cargo check` and `cargo clippy` on the generated code.
96
+ - **Zero Warning Policy**: Code that generates warnings in clippy is considered a failure.
94
97
 
95
- ## Workflows
98
+ ---
96
99
 
97
- For each task you complete,
98
- please confirm there are no errors by writing a test for your task,
99
- then running `npm test` and `just check-rust`.
100
- After ensuring everything works as expected, run `npm run format` and `npm run lint`
101
- and ensure there are no linting errors or warnings.
100
+ ## 5. Coding Standards & Maintenance
102
101
 
103
- Next, increment the version in package.json,
104
- write the changelog of what you did,
105
- then run `just publish` to publish the new version to the npm registry.
102
+ ### Rust Best Practices
106
103
 
107
- Increment version guide: minor for backward-compatible new features, major for breaking changes, patch otherwise.
104
+ - **Idiomatic Code**: Use standard Rust patterns (e.g., `Result` for errors, `Option` for nullables).
105
+ - **Pluggability**: Prefer Traits over hardcoded implementations (see `EtagCache` trait).
106
+ - **Documentation**: Generated code should include `///` doc comments derived from TypeSpec `@doc` decorators.
108
107
 
109
- Finally, run `git add -A`, `git commit`, `git tag vx.x.x`, `git push` to push the commit,
110
- and `git push --tags` to push the tags to the remote.
108
+ ### Maintenance Guidelines
111
109
 
112
- ## Release (let user handle)
110
+ - **Surgical Changes**: Only modify the files directly related to your task. Avoid "cleaning up" adjacent files.
111
+ - **Backward Compatibility**: Ensure that new features do not break existing Rust compilation in the example project.
112
+ - **Dependencies**: New Rust dependencies in generated code must be added to `example/output-rust/Cargo.toml`.
113
113
 
114
- When releasing a new version:
114
+ ---
115
115
 
116
- 1. Bump `version` in `package.json`
117
- 2. Update `CHANGELOG.md` (include issue refs)
118
- 3. Run `just publish`
116
+ ## 6. Release Process
119
117
 
120
- Do not publish yourself—inform user when ready for release.
118
+ 1. **Version Bump**: Increment version in `package.json` (SemVer).
119
+ 2. **Changelog**: Update `CHANGELOG.md` with a concise summary.
120
+ 3. **Final Verification**: Run `just check-all`.
121
+ 4. **Git commit**: Run `git commit -m "Your message"`, `git tag v<MAJOR>.<MINOR>.<PATCH>`, `git push && git push --tags`
122
+ 5. **Publish**: (To be handled by repository owners) `just publish`.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.13.0] - 2026-05-12
9
+
10
+ ### Added
11
+
12
+ - **@cacheControl decorator**: New decorator to define `Cache-Control` headers for operations.
13
+ - Example: `@cacheControl("public, max-age=3600")` adds the corresponding header to the response.
14
+ - **Enhanced @etagCache decorator**: Refactored to support an optional `etagKey`.
15
+ - If provided, `etagKey` is used as the cache key base (e.g., `@etagCache("my-key")`).
16
+ - Otherwise, defaults to an operation-specific key based on path parameters.
17
+ - **Golden File Testing**: Introduced a robust testing framework using "Golden Files" to ensure deterministic output and easier verification of generator changes.
18
+ - **Modular Pipeline Architecture**: Major internal refactor decomposing the monolithic emitter into decoupled modules (`parser`, `generator`, `decorators`, `models`, `formatter`).
19
+
20
+ ### Fixed
21
+
22
+ - **Clippy hygiene**: Fixed multiple clippy warnings in generated code, including `let_and_return`, `needless_borrow`, and `collapsible_if`.
23
+ - **Response Headers**: Fixed an issue where `ETag` headers from the cache were not being properly injected into the response on cache-miss (but valid content) paths.
24
+
8
25
  ## [0.12.0] - 2026-05-11
9
26
 
10
27
  ### Added
@@ -0,0 +1,6 @@
1
+ import { DecoratorContext, Operation } from "@typespec/compiler";
2
+ /**
3
+ * @cacheControl decorator implementation.
4
+ * Stores the cache control value on the operation.
5
+ */
6
+ export declare function $cacheControl(context: DecoratorContext, target: Operation, value: string): void;
@@ -0,0 +1,9 @@
1
+ import { cacheControlKey } from "../models/keys.js";
2
+ /**
3
+ * @cacheControl decorator implementation.
4
+ * Stores the cache control value on the operation.
5
+ */
6
+ export function $cacheControl(context, target, value) {
7
+ context.program.stateMap(cacheControlKey).set(target, value);
8
+ }
9
+ //# sourceMappingURL=cache_control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache_control.js","sourceRoot":"","sources":["../../../src/decorators/cache_control.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAyB,EACzB,MAAiB,EACjB,KAAa;IAEb,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { DecoratorContext, Operation } from "@typespec/compiler";
2
+ /**
3
+ * @etagCache decorator implementation.
4
+ * Stores the optional etagKey on the operation.
5
+ */
6
+ export declare function $etagCache(context: DecoratorContext, target: Operation, etagKey?: string): void;
@@ -0,0 +1,9 @@
1
+ import { etagCacheKey } from "../models/keys.js";
2
+ /**
3
+ * @etagCache decorator implementation.
4
+ * Stores the optional etagKey on the operation.
5
+ */
6
+ export function $etagCache(context, target, etagKey) {
7
+ context.program.stateMap(etagCacheKey).set(target, etagKey ?? true);
8
+ }
9
+ //# sourceMappingURL=etag_cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag_cache.js","sourceRoot":"","sources":["../../../src/decorators/etag_cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,OAAyB,EACzB,MAAiB,EACjB,OAAgB;IAEhB,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./rust_derive.js";
2
+ export * from "./rust_attr.js";
3
+ export * from "./rust_impl.js";
4
+ export * from "./rust_self.js";
5
+ export * from "./etag_cache.js";
6
+ export * from "./cache_control.js";
@@ -0,0 +1,7 @@
1
+ export * from "./rust_derive.js";
2
+ export * from "./rust_attr.js";
3
+ export * from "./rust_impl.js";
4
+ export * from "./rust_self.js";
5
+ export * from "./etag_cache.js";
6
+ export * from "./cache_control.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DecoratorContext, Type } from "@typespec/compiler";
2
+ export declare function $rustAttr(context: DecoratorContext, target: Type, attr: string): void;
3
+ export declare function $rustAttrs(context: DecoratorContext, target: Type, ...attrs: string[]): void;
@@ -0,0 +1,45 @@
1
+ import { getNamespaceFullName, } from "@typespec/compiler";
2
+ import { rustAttrKey } from "../models/keys.js";
3
+ export function $rustAttr(context, target, attr) {
4
+ if (target.kind !== "Model" &&
5
+ target.kind !== "Enum" &&
6
+ target.kind !== "ModelProperty") {
7
+ context.program.reportDiagnostic({
8
+ code: "rust-attr-invalid-target",
9
+ message: `@rustAttr can only be applied to models, enums, and model properties`,
10
+ severity: "error",
11
+ target: context.decoratorTarget,
12
+ });
13
+ return;
14
+ }
15
+ let ns;
16
+ if (target.kind === "Model") {
17
+ ns = target.namespace;
18
+ }
19
+ else if (target.kind === "Enum") {
20
+ ns = target.namespace;
21
+ }
22
+ else if (target.kind === "ModelProperty") {
23
+ ns = target.model?.namespace;
24
+ }
25
+ const nsFullName = ns ? getNamespaceFullName(ns) : "";
26
+ if (!nsFullName.startsWith("TypeSpec")) {
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const info = target[rustAttrKey];
29
+ if (info) {
30
+ if (!info.attrs.includes(attr)) {
31
+ info.attrs.push(attr);
32
+ }
33
+ }
34
+ else {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ target[rustAttrKey] = { attrs: [attr] };
37
+ }
38
+ }
39
+ }
40
+ export function $rustAttrs(context, target, ...attrs) {
41
+ for (const attr of attrs) {
42
+ $rustAttr(context, target, attr);
43
+ }
44
+ }
45
+ //# sourceMappingURL=rust_attr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_attr.js","sourceRoot":"","sources":["../../../src/decorators/rust_attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,GAGrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,UAAU,SAAS,CACvB,OAAyB,EACzB,MAAY,EACZ,IAAY;IAEZ,IACE,MAAM,CAAC,IAAI,KAAK,OAAO;QACvB,MAAM,CAAC,IAAI,KAAK,MAAM;QACtB,MAAM,CAAC,IAAI,KAAK,eAAe,EAC/B,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC/B,IAAI,EAAE,0BAA0B;YAChC,OAAO,EAAE,sEAAsE;YAC/E,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,OAAO,CAAC,eAAe;SAChC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,IAAI,EAAyB,CAAC;IAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IACxB,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAClC,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IACxB,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QAC3C,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC;IAC/B,CAAC;IAED,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACvC,8DAA8D;QAC9D,MAAM,IAAI,GAAI,MAAc,CAAC,WAAW,CAA6B,CAAC;QACtE,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,8DAA8D;YAC7D,MAAc,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,OAAyB,EACzB,MAAY,EACZ,GAAG,KAAe;IAElB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DecoratorContext, Type } from "@typespec/compiler";
2
+ export declare function $rustDerive(context: DecoratorContext, target: Type, derive: string): void;
3
+ export declare function $rustDerives(context: DecoratorContext, target: Type, ...derives: string[]): void;
@@ -0,0 +1,39 @@
1
+ import { getNamespaceFullName, } from "@typespec/compiler";
2
+ import { rustDeriveKey } from "../models/keys.js";
3
+ export function $rustDerive(context, target, derive) {
4
+ if (target.kind !== "Model" && target.kind !== "Enum") {
5
+ context.program.reportDiagnostic({
6
+ code: "rust-derive-invalid-target",
7
+ message: `@rustDerive can only be applied to models and enums`,
8
+ severity: "error",
9
+ target: context.decoratorTarget,
10
+ });
11
+ return;
12
+ }
13
+ const ns = target.kind === "Model"
14
+ ? target.namespace
15
+ ? getNamespaceFullName(target.namespace)
16
+ : ""
17
+ : target.namespace
18
+ ? getNamespaceFullName(target.namespace)
19
+ : "";
20
+ if (!ns.startsWith("TypeSpec")) {
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ const info = target[rustDeriveKey];
23
+ if (info) {
24
+ if (!info.derives.includes(derive)) {
25
+ info.derives.push(derive);
26
+ }
27
+ }
28
+ else {
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ target[rustDeriveKey] = { derives: [derive] };
31
+ }
32
+ }
33
+ }
34
+ export function $rustDerives(context, target, ...derives) {
35
+ for (const derive of derives) {
36
+ $rustDerive(context, target, derive);
37
+ }
38
+ }
39
+ //# sourceMappingURL=rust_derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_derive.js","sourceRoot":"","sources":["../../../src/decorators/rust_derive.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,MAAM,UAAU,WAAW,CACzB,OAAyB,EACzB,MAAY,EACZ,MAAc;IAEd,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtD,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC/B,IAAI,EAAE,4BAA4B;YAClC,OAAO,EAAE,qDAAqD;YAC9D,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,OAAO,CAAC,eAAe;SAChC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GACN,MAAM,CAAC,IAAI,KAAK,OAAO;QACrB,CAAC,CAAC,MAAM,CAAC,SAAS;YAChB,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC;YACxC,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,MAAM,CAAC,SAAS;YAChB,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC;YACxC,CAAC,CAAC,EAAE,CAAC;IAEX,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,8DAA8D;QAC9D,MAAM,IAAI,GAAI,MAAc,CAAC,aAAa,CAA+B,CAAC;QAC1E,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,8DAA8D;YAC7D,MAAc,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,OAAyB,EACzB,MAAY,EACZ,GAAG,OAAiB;IAEpB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { DecoratorContext, Type } from "@typespec/compiler";
2
+ export declare function $rustImpl(context: DecoratorContext, target: Type, impl: string): void;
@@ -0,0 +1,19 @@
1
+ import { getNamespaceFullName, } from "@typespec/compiler";
2
+ import { rustImplKey } from "../models/keys.js";
3
+ export function $rustImpl(context, target, impl) {
4
+ if (target.kind !== "Model") {
5
+ context.program.reportDiagnostic({
6
+ code: "rust-impl-invalid-target",
7
+ message: `@rustImpl can only be applied to models`,
8
+ severity: "error",
9
+ target: context.decoratorTarget,
10
+ });
11
+ return;
12
+ }
13
+ const ns = target.namespace ? getNamespaceFullName(target.namespace) : "";
14
+ if (!ns.startsWith("TypeSpec")) {
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ target[rustImplKey] = { impl: impl };
17
+ }
18
+ }
19
+ //# sourceMappingURL=rust_impl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_impl.js","sourceRoot":"","sources":["../../../src/decorators/rust_impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,SAAS,CACvB,OAAyB,EACzB,MAAY,EACZ,IAAY;IAEZ,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC/B,IAAI,EAAE,0BAA0B;YAChC,OAAO,EAAE,yCAAyC;YAClD,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,OAAO,CAAC,eAAe;SAChC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,8DAA8D;QAC7D,MAAc,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DecoratorContext, Type } from "@typespec/compiler";
2
+ export declare function $rustMut(context: DecoratorContext, target: Type): void;
3
+ export declare function $rustOwn(context: DecoratorContext, target: Type): void;
@@ -0,0 +1,35 @@
1
+ import { getNamespaceFullName, } from "@typespec/compiler";
2
+ import { rustSelfReceiverKey } from "../models/keys.js";
3
+ export function $rustMut(context, target) {
4
+ if (target.kind !== "Operation") {
5
+ context.program.reportDiagnostic({
6
+ code: "rust-mut-invalid-target",
7
+ message: `@rustMut can only be applied to operations`,
8
+ severity: "error",
9
+ target: context.decoratorTarget,
10
+ });
11
+ return;
12
+ }
13
+ const ns = target.namespace ? getNamespaceFullName(target.namespace) : "";
14
+ if (!ns.startsWith("TypeSpec")) {
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ target[rustSelfReceiverKey] = "&mut self";
17
+ }
18
+ }
19
+ export function $rustOwn(context, target) {
20
+ if (target.kind !== "Operation") {
21
+ context.program.reportDiagnostic({
22
+ code: "rust-own-invalid-target",
23
+ message: `@rustOwn can only be applied to operations`,
24
+ severity: "error",
25
+ target: context.decoratorTarget,
26
+ });
27
+ return;
28
+ }
29
+ const ns = target.namespace ? getNamespaceFullName(target.namespace) : "";
30
+ if (!ns.startsWith("TypeSpec")) {
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ target[rustSelfReceiverKey] = "self";
33
+ }
34
+ }
35
+ //# sourceMappingURL=rust_self.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_self.js","sourceRoot":"","sources":["../../../src/decorators/rust_self.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,UAAU,QAAQ,CAAC,OAAyB,EAAE,MAAY;IAC9D,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC/B,IAAI,EAAE,yBAAyB;YAC/B,OAAO,EAAE,4CAA4C;YACrD,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,OAAO,CAAC,eAAe;SAChC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,8DAA8D;QAC7D,MAAc,CAAC,mBAAmB,CAAC,GAAG,WAAW,CAAC;IACrD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAyB,EAAE,MAAY;IAC9D,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC/B,IAAI,EAAE,yBAAyB;YAC/B,OAAO,EAAE,4CAA4C;YACrD,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,OAAO,CAAC,eAAe;SAChC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,8DAA8D;QAC7D,MAAc,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -1,12 +1,3 @@
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 $rustAttr(context: DecoratorContext, target: Type, attr: string): void;
8
- export declare function $rustAttrs(context: DecoratorContext, target: Type, ...attrs: string[]): void;
9
- export declare function $rustImpl(context: DecoratorContext, target: Type, impl: string): void;
10
- export declare function $rustMut(context: DecoratorContext, target: Type): void;
11
- export declare function $rustOwn(context: DecoratorContext, target: Type): void;
1
+ import { EmitContext } from "@typespec/compiler";
2
+ import { RustEmitterOptions } from "./models/types.js";
12
3
  export declare function $onEmit(context: EmitContext<RustEmitterOptions>, _options?: RustEmitterOptions): Promise<void>;