typespec-rust-emitter 0.13.1 → 0.14.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,2 @@
1
+ # Generated Rust output is formatted and regenerated by the Rust/TypeSpec pipeline.
2
+ example/output-rust
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ 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.14.0] - 2026-05-12
9
+
10
+ ### Changed
11
+
12
+ - **ETag cache generation**: `@etagCache` now emits an async cache trait, typed `If-None-Match` comparison, and safer cached header handling.
13
+ - **Cache key scoping**: Generated ETag keys now interpolate route parameters and append path params by default when the decorator key is static.
14
+ - **Example coverage**: The demo spec now includes route-parameter interpolation and multi-parameter cache-key examples.
15
+
8
16
  ## [0.13.1] - 2026-05-12
9
17
 
10
18
  ### Fixed
@@ -1,3 +1,4 @@
1
+ import type { ParameterInfo } from "../models/types.js";
1
2
  /**
2
3
  * generateEtagCacheTrait
3
4
  *
@@ -11,14 +12,14 @@ export declare function generateEtagCacheTrait(): string;
11
12
  * Returns the Rust source for a single ETag-aware axum handler.
12
13
  *
13
14
  * @param handlerFnName snake_case name, e.g. "articles_get_article"
14
- * @param cacheKey format string for the cache key, e.g. "articles_get_article:{id}"
15
+ * @param pathParams route path parameters used for cache key scoping
15
16
  * @param extractorLines axum extractors needed by the operation
16
17
  * @param serverArgsStr comma-separated args forwarded to the trait method
17
18
  * @param responseName PascalCase name of the response enum, e.g. "ArticlesGetArticleResponse"
18
19
  */
19
20
  export declare function generateEtagHandler(opts: {
20
21
  handlerFnName: string;
21
- cacheKey: string;
22
+ pathParams: ParameterInfo[];
22
23
  extractorLines: string[];
23
24
  serverArgsStr: string;
24
25
  responseName: string;
@@ -7,37 +7,72 @@
7
7
  export function generateEtagCacheTrait() {
8
8
  return `/// Pluggable ETag cache backend.
9
9
  /// Implement this for Redis, Memcached, in-memory HashMap, or any store.
10
+ #[async_trait]
10
11
  pub trait EtagCache {
11
12
  /// Return the stored ETag string for \`key\`, or \`None\` if not cached.
12
- fn get(&self, key: &str) -> Option<String>;
13
+ async fn get(&self, key: &str) -> Option<String>;
13
14
  /// Store \`etag\` under \`key\`.
14
- fn set(&self, key: &str, etag: &str);
15
+ async fn set(&self, key: &str, etag: &str);
15
16
  }
16
17
  `;
17
18
  }
19
+ function rustStringLiteral(value) {
20
+ return `"${value
21
+ .replace(/\\/g, "\\\\")
22
+ .replace(/"/g, '\\"')
23
+ .replace(/\n/g, "\\n")
24
+ .replace(/\r/g, "\\r")
25
+ .replace(/\t/g, "\\t")}"`;
26
+ }
27
+ function buildCacheKeyExpression(opts) {
28
+ const { handlerFnName, etagKey, pathParams } = opts;
29
+ const pathSuffix = pathParams.map((p) => `{${p.rustName}}`).join(":");
30
+ const paramNames = new Map();
31
+ for (const param of pathParams) {
32
+ paramNames.set(param.name, param.rustName);
33
+ paramNames.set(param.rustName, param.rustName);
34
+ }
35
+ let template = etagKey ?? handlerFnName;
36
+ template = template.replace(/\{([^}]+)\}/g, (match, name) => {
37
+ const rustName = paramNames.get(name);
38
+ return rustName ? `{${rustName}}` : match;
39
+ });
40
+ const hasExplicitPathParam = pathParams.some((param) => template.includes(`{${param.name}}`) ||
41
+ template.includes(`{${param.rustName}}`));
42
+ if (pathSuffix && !hasExplicitPathParam) {
43
+ template = `${template}:${pathSuffix}`;
44
+ }
45
+ if (!template.includes("{")) {
46
+ return rustStringLiteral(template);
47
+ }
48
+ return `format!(${rustStringLiteral(template)})`;
49
+ }
18
50
  /**
19
51
  * generateEtagHandler
20
52
  *
21
53
  * Returns the Rust source for a single ETag-aware axum handler.
22
54
  *
23
55
  * @param handlerFnName snake_case name, e.g. "articles_get_article"
24
- * @param cacheKey format string for the cache key, e.g. "articles_get_article:{id}"
56
+ * @param pathParams route path parameters used for cache key scoping
25
57
  * @param extractorLines axum extractors needed by the operation
26
58
  * @param serverArgsStr comma-separated args forwarded to the trait method
27
59
  * @param responseName PascalCase name of the response enum, e.g. "ArticlesGetArticleResponse"
28
60
  */
29
61
  export function generateEtagHandler(opts) {
30
- const { handlerFnName, cacheKey, extractorLines, serverArgsStr, etagKey, cacheControl, serviceBinding = "service", } = opts;
31
- const effectiveCacheKey = etagKey ? `"${etagKey}"` : `format!("${cacheKey}")`;
62
+ const { handlerFnName, pathParams, extractorLines, serverArgsStr, etagKey, cacheControl, serviceBinding = "service", } = opts;
63
+ const effectiveCacheKey = buildCacheKeyExpression({
64
+ handlerFnName,
65
+ etagKey,
66
+ pathParams,
67
+ });
32
68
  const operationExtractors = extractorLines.length > 0 ? `${extractorLines.join("\n")}\n` : "";
33
69
  const finalResponseLogic = ` match result {
34
70
  Ok(response) => {
35
71
  let mut res = response.into_response();
36
- if let Some(stored_etag) = cache.get(cache_key.as_ref()) {
37
- res.headers_mut().insert(
38
- axum::http::header::ETAG,
39
- axum::http::HeaderValue::from_str(&stored_etag).unwrap(),
40
- );
72
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
73
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
74
+ {
75
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
41
76
  }
42
77
  ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
43
78
  res
@@ -60,13 +95,14 @@ where
60
95
  {
61
96
  // Check If-None-Match against the cache
62
97
  let cache_key = ${effectiveCacheKey};
63
- if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) = (cache.get(cache_key.as_ref()), if_none_match) {
64
- // If the client's ETag matches, respond 304 immediately
65
- if stored_etag == format!("{:?}", inm).trim_matches('"') {
66
- let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
67
- ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
68
- return res;
69
- }
98
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
99
+ (cache.get(cache_key.as_ref()).await, if_none_match)
100
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
101
+ && !inm.precondition_passes(&etag)
102
+ {
103
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
104
+ ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
105
+ return res;
70
106
  }
71
107
  // Forward to business logic
72
108
  let result = ${serviceBinding}.${handlerFnName}(${serverArgsStr}).await;
@@ -1 +1 @@
1
- {"version":3,"file":"etag_router.js","sourceRoot":"","sources":["../../../src/generator/etag_router.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;CAQR,CAAC;AACF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,IASnC;IACC,MAAM,EACJ,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,EACb,OAAO,EACP,YAAY,EACZ,cAAc,GAAG,SAAS,GAC3B,GAAG,IAAI,CAAC;IAET,MAAM,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,YAAY,QAAQ,IAAI,CAAC;IAC9E,MAAM,mBAAmB,GACvB,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpE,MAAM,kBAAkB,GAAG;;;;;;;;;cASf,YAAY,CAAC,CAAC,CAAC,qGAAqG,YAAY,MAAM,CAAC,CAAC,CAAC,EAAE;;;;;;;;MAQnJ,CAAC;IAEL,OAAO,gBAAgB,aAAa;2BACX,cAAc;;;EAGvC,mBAAmB;;;;;;;sBAOC,iBAAiB;;;;;cAKzB,YAAY,CAAC,CAAC,CAAC,qGAAqG,YAAY,MAAM,CAAC,CAAC,CAAC,EAAE;;;;;mBAKtI,cAAc,IAAI,aAAa,IAAI,aAAa;EACjE,kBAAkB;EAClB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"etag_router.js","sourceRoot":"","sources":["../../../src/generator/etag_router.ts"],"names":[],"mappings":"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"}
@@ -76,13 +76,12 @@ export function generateRouter(program, namespaceGroups, anonymousEnums) {
76
76
  }
77
77
  const serverArgsStr = serverArgs.join(", ");
78
78
  if (hasEtagCache(program, op)) {
79
- const cacheKey = `${handlerFnName}:${pathParams.map((p) => `{${p.rustName}}`).join(":")}`;
80
79
  const cachedServerArgs = isProtected
81
80
  ? [serverArgs[0], "&cache", ...serverArgs.slice(1)]
82
81
  : ["&cache", ...serverArgs];
83
82
  const code = generateEtagHandler({
84
83
  handlerFnName,
85
- cacheKey,
84
+ pathParams,
86
85
  extractorLines,
87
86
  serverArgsStr: cachedServerArgs.join(", "),
88
87
  responseName: `${nsName}${toPascalCase(opInfo.name)}Response`,
@@ -102,7 +101,7 @@ export function generateRouter(program, namespaceGroups, anonymousEnums) {
102
101
  continue;
103
102
  }
104
103
  const serverCall = `service.${traitFnName}(${serverArgsStr}).await`;
105
- let responseHandling = opInfo.cacheControl
104
+ const responseHandling = opInfo.cacheControl
106
105
  ? `match result {
107
106
  Ok(response) => {
108
107
  let mut res = response.into_response();
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/generator/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/generator/router.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server_trait.js","sourceRoot":"","sources":["../../../src/generator/server_trait.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server_trait.js","sourceRoot":"","sources":["../../../src/generator/server_trait.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/parser/operations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/parser/operations.ts"],"names":[],"mappings":"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"}
@@ -25,14 +25,17 @@ describe("@etagCache decorator", () => {
25
25
  const results = await emit(ETAG_SPEC);
26
26
  const server = results["server.rs"];
27
27
  strictEqual(server.includes("pub trait EtagCache"), true);
28
- strictEqual(server.includes("fn get(&self, key: &str) -> Option<String>"), true);
29
- strictEqual(server.includes("fn set(&self, key: &str, etag: &str)"), true);
28
+ strictEqual(server.includes("async fn get(&self, key: &str) -> Option<String>"), true);
29
+ strictEqual(server.includes("async fn set(&self, key: &str, etag: &str)"), true);
30
30
  });
31
31
  it("generates cache-aware handler with 304 short-circuit", async () => {
32
32
  const results = await emit(ETAG_SPEC);
33
33
  const server = results["server.rs"];
34
34
  strictEqual(server.includes("axum::http::StatusCode::NOT_MODIFIED"), true);
35
- strictEqual(server.includes("cache.get(cache_key.as_ref())"), true);
35
+ strictEqual(server.includes("cache.get(cache_key.as_ref()).await"), true);
36
+ strictEqual(server.includes("stored_etag.parse::<axum_extra::headers::ETag>()"), true);
37
+ strictEqual(server.includes("!inm.precondition_passes(&etag)"), true);
38
+ strictEqual(server.includes("HeaderValue::from_str(&stored_etag).unwrap()"), false);
36
39
  });
37
40
  it("supports custom etagKey and @cacheControl", async () => {
38
41
  const spec = `
@@ -51,9 +54,43 @@ describe("@etagCache decorator", () => {
51
54
  const results = await emit(spec);
52
55
  const server = results["server.rs"];
53
56
  strictEqual(server.includes('let cache_key = "my-key";'), true);
54
- strictEqual(server.includes('axum::http::header::CACHE_CONTROL'), true);
57
+ strictEqual(server.includes("axum::http::header::CACHE_CONTROL"), true);
55
58
  strictEqual(server.includes('"public"'), true);
56
59
  });
60
+ it("interpolates path parameters in custom etag keys", async () => {
61
+ const spec = `
62
+ import "@typespec/http";
63
+ import "typespec-rust-emitter";
64
+ using TypeSpec.Http;
65
+
66
+ @route("/accounts/{accountId}/images")
67
+ namespace Images {
68
+ @etagCache("img:list:pub:{accountId}")
69
+ @get
70
+ op listPublic(@path accountId: string): { @statusCode statusCode: 200; };
71
+ }
72
+ `;
73
+ const results = await emit(spec);
74
+ const server = results["server.rs"];
75
+ strictEqual(server.includes('let cache_key = format!("img:list:pub:{account_id}");'), true);
76
+ });
77
+ it("appends path parameters to static etag keys", async () => {
78
+ const spec = `
79
+ import "@typespec/http";
80
+ import "typespec-rust-emitter";
81
+ using TypeSpec.Http;
82
+
83
+ @route("/accounts/{accountId}/images")
84
+ namespace Images {
85
+ @etagCache("img:list:pub")
86
+ @get
87
+ op listPublic(@path accountId: string): { @statusCode statusCode: 200; };
88
+ }
89
+ `;
90
+ const results = await emit(spec);
91
+ const server = results["server.rs"];
92
+ strictEqual(server.includes('let cache_key = format!("img:list:pub:{account_id}");'), true);
93
+ });
57
94
  it("orders cache after claims when combined with @useAuth", async () => {
58
95
  const spec = `
59
96
  import "@typespec/http";
@@ -1 +1 @@
1
- {"version":3,"file":"etag_cache.test.js","sourceRoot":"","sources":["../../test/etag_cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;CAkBjB,CAAC;AAEF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,WAAW,CACT,MAAM,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAC7D,IAAI,CACL,CAAC;QACF,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC3E,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,IAAI,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,IAAI,GAAG;;;;;;;;;;;;KAYZ,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,2BAA2B,CAAC,EAAE,IAAI,CAAC,CAAC;QAChE,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,IAAI,CAAC,CAAC;QACxE,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;KAiBZ,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,WAAW,CACT,MAAM,CAAC,QAAQ,CACb,wHAAwH,CACzH,EACD,IAAI,CACL,CAAC;QACF,WAAW,CACT,MAAM,CAAC,QAAQ,CACb,sEAAsE,CACvE,EACD,IAAI,CACL,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"etag_cache.test.js","sourceRoot":"","sources":["../../test/etag_cache.test.ts"],"names":[],"mappings":"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"}
package/example/main.tsp CHANGED
@@ -74,6 +74,30 @@ namespace Items {
74
74
  };
75
75
  }
76
76
 
77
+ @route("/accounts/{accountId}/images")
78
+ namespace Images {
79
+ @etagCache("img:list:pub:{accountId}")
80
+ @cacheControl("no-cache, max-age=2592000")
81
+ @get
82
+ op listPublic(@path accountId: Uuid): {
83
+ @statusCode statusCode: 200;
84
+ @body images: Item[];
85
+ } | {
86
+ @statusCode statusCode: 304;
87
+ };
88
+
89
+ @etagCache("img:detail:pub")
90
+ @cacheControl("public, max-age=60")
91
+ @get
92
+ @route("/{imageId}")
93
+ op getImage(@path accountId: Uuid, @path imageId: string): {
94
+ @statusCode statusCode: 200;
95
+ @body image: Item;
96
+ } | {
97
+ @statusCode statusCode: 304;
98
+ };
99
+ }
100
+
77
101
  model Article {
78
102
  id: string;
79
103
  title: string;
@@ -12,11 +12,12 @@ use eyre::Result;
12
12
 
13
13
  /// Pluggable ETag cache backend.
14
14
  /// Implement this for Redis, Memcached, in-memory HashMap, or any store.
15
+ #[async_trait]
15
16
  pub trait EtagCache {
16
17
  /// Return the stored ETag string for `key`, or `None` if not cached.
17
- fn get(&self, key: &str) -> Option<String>;
18
+ async fn get(&self, key: &str) -> Option<String>;
18
19
  /// Store `etag` under `key`.
19
- fn set(&self, key: &str, etag: &str);
20
+ async fn set(&self, key: &str, etag: &str);
20
21
  }
21
22
 
22
23
 
@@ -34,6 +35,12 @@ pub trait Server: Send + Sync {
34
35
  async fn items_get_item(&self, id: String) -> Result<ItemsGetItemResponse>;
35
36
  async fn items_create_item(&mut self, body: Item) -> Result<ItemsCreateItemResponse>;
36
37
  async fn items_update_item(&mut self, id: String, body: Item) -> Result<ItemsUpdateItemResponse>;
38
+ async fn images_list_public<C: EtagCache + Send + Sync>(
39
+ &self, cache: &C, account_id: uuid::Uuid
40
+ ) -> Result<ImagesListPublicResponse>;
41
+ async fn images_get_image<C: EtagCache + Send + Sync>(
42
+ &self, cache: &C, account_id: uuid::Uuid, image_id: String
43
+ ) -> Result<ImagesGetImageResponse>;
37
44
  async fn articles_get_article<C: EtagCache + Send + Sync>(
38
45
  &self, claims: Self::Claims, cache: &C, id: String
39
46
  ) -> Result<ArticlesGetArticleResponse>;
@@ -124,6 +131,38 @@ impl IntoResponse for ItemsUpdateItemResponse {
124
131
  }
125
132
  }
126
133
 
134
+ #[allow(clippy::type_complexity)]
135
+ pub enum ImagesListPublicResponse {
136
+ Ok(Json<Vec<Item>>),
137
+ NotModified,
138
+ }
139
+
140
+ impl IntoResponse for ImagesListPublicResponse {
141
+ fn into_response(self) -> axum::response::Response {
142
+ match self {
143
+
144
+ ImagesListPublicResponse::Ok(body) => (StatusCode::OK, body).into_response(),
145
+ ImagesListPublicResponse::NotModified => StatusCode::NOT_MODIFIED.into_response(),
146
+ }
147
+ }
148
+ }
149
+
150
+ #[allow(clippy::type_complexity)]
151
+ pub enum ImagesGetImageResponse {
152
+ Ok(Json<Item>),
153
+ NotModified,
154
+ }
155
+
156
+ impl IntoResponse for ImagesGetImageResponse {
157
+ fn into_response(self) -> axum::response::Response {
158
+ match self {
159
+
160
+ ImagesGetImageResponse::Ok(body) => (StatusCode::OK, body).into_response(),
161
+ ImagesGetImageResponse::NotModified => StatusCode::NOT_MODIFIED.into_response(),
162
+ }
163
+ }
164
+ }
165
+
127
166
  #[allow(clippy::type_complexity)]
128
167
  pub enum ArticlesGetArticleResponse {
129
168
  Ok(Json<Article>),
@@ -249,24 +288,24 @@ where
249
288
  {
250
289
  // Check If-None-Match against the cache
251
290
  let cache_key = "article-list";
252
- if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) = (cache.get(cache_key.as_ref()), if_none_match) {
253
- // If the client's ETag matches, respond 304 immediately
254
- if stored_etag == format!("{:?}", inm).trim_matches('"') {
255
- let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
256
- res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
257
- return res;
258
- }
291
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
292
+ (cache.get(cache_key.as_ref()).await, if_none_match)
293
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
294
+ && !inm.precondition_passes(&etag)
295
+ {
296
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
297
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
298
+ return res;
259
299
  }
260
300
  // Forward to business logic
261
301
  let result = service.items_list(&cache).await;
262
302
  match result {
263
303
  Ok(response) => {
264
304
  let mut res = response.into_response();
265
- if let Some(stored_etag) = cache.get(cache_key.as_ref()) {
266
- res.headers_mut().insert(
267
- axum::http::header::ETAG,
268
- axum::http::HeaderValue::from_str(&stored_etag).unwrap(),
269
- );
305
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
306
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
307
+ {
308
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
270
309
  }
271
310
  res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
272
311
  res
@@ -344,6 +383,92 @@ where
344
383
  }
345
384
  }
346
385
 
386
+ pub async fn images_list_public_handler<S, C>(
387
+ axum::extract::State(service): axum::extract::State<S>,
388
+ axum::extract::State(cache): axum::extract::State<C>,
389
+ if_none_match: Option<axum_extra::TypedHeader<axum_extra::headers::IfNoneMatch>>,
390
+ Path(account_id): Path<uuid::Uuid>,
391
+ ) -> impl axum::response::IntoResponse
392
+ where
393
+ S: Server + Send + Sync + 'static,
394
+ S::Claims: Send + Sync + Clone + 'static,
395
+ C: EtagCache + Clone + Send + Sync + 'static,
396
+ {
397
+ // Check If-None-Match against the cache
398
+ let cache_key = format!("img:list:pub:{account_id}");
399
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
400
+ (cache.get(cache_key.as_ref()).await, if_none_match)
401
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
402
+ && !inm.precondition_passes(&etag)
403
+ {
404
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
405
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("no-cache, max-age=2592000"));
406
+ return res;
407
+ }
408
+ // Forward to business logic
409
+ let result = service.images_list_public(&cache, account_id).await;
410
+ match result {
411
+ Ok(response) => {
412
+ let mut res = response.into_response();
413
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
414
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
415
+ {
416
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
417
+ }
418
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("no-cache, max-age=2592000"));
419
+ res
420
+ }
421
+ Err(e) => (
422
+ axum::http::StatusCode::INTERNAL_SERVER_ERROR,
423
+ format!("Internal error: {e}"),
424
+ )
425
+ .into_response(),
426
+ }
427
+ }
428
+
429
+ pub async fn images_get_image_handler<S, C>(
430
+ axum::extract::State(service): axum::extract::State<S>,
431
+ axum::extract::State(cache): axum::extract::State<C>,
432
+ if_none_match: Option<axum_extra::TypedHeader<axum_extra::headers::IfNoneMatch>>,
433
+ Path((account_id, image_id)): Path<(uuid::Uuid, String)>,
434
+ ) -> impl axum::response::IntoResponse
435
+ where
436
+ S: Server + Send + Sync + 'static,
437
+ S::Claims: Send + Sync + Clone + 'static,
438
+ C: EtagCache + Clone + Send + Sync + 'static,
439
+ {
440
+ // Check If-None-Match against the cache
441
+ let cache_key = format!("img:detail:pub:{account_id}:{image_id}");
442
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
443
+ (cache.get(cache_key.as_ref()).await, if_none_match)
444
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
445
+ && !inm.precondition_passes(&etag)
446
+ {
447
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
448
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=60"));
449
+ return res;
450
+ }
451
+ // Forward to business logic
452
+ let result = service.images_get_image(&cache, account_id, image_id).await;
453
+ match result {
454
+ Ok(response) => {
455
+ let mut res = response.into_response();
456
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
457
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
458
+ {
459
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
460
+ }
461
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=60"));
462
+ res
463
+ }
464
+ Err(e) => (
465
+ axum::http::StatusCode::INTERNAL_SERVER_ERROR,
466
+ format!("Internal error: {e}"),
467
+ )
468
+ .into_response(),
469
+ }
470
+ }
471
+
347
472
  pub async fn articles_get_article_handler<S, C>(
348
473
  axum::extract::State(service): axum::extract::State<S>,
349
474
  axum::extract::State(cache): axum::extract::State<C>,
@@ -357,25 +482,25 @@ where
357
482
  C: EtagCache + Clone + Send + Sync + 'static,
358
483
  {
359
484
  // Check If-None-Match against the cache
360
- let cache_key = "article-list";
361
- if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) = (cache.get(cache_key.as_ref()), if_none_match) {
362
- // If the client's ETag matches, respond 304 immediately
363
- if stored_etag == format!("{:?}", inm).trim_matches('"') {
364
- let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
365
- res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
366
- return res;
367
- }
485
+ let cache_key = format!("article-list:{id}");
486
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
487
+ (cache.get(cache_key.as_ref()).await, if_none_match)
488
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
489
+ && !inm.precondition_passes(&etag)
490
+ {
491
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
492
+ res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
493
+ return res;
368
494
  }
369
495
  // Forward to business logic
370
496
  let result = service.articles_get_article(claims, &cache, id).await;
371
497
  match result {
372
498
  Ok(response) => {
373
499
  let mut res = response.into_response();
374
- if let Some(stored_etag) = cache.get(cache_key.as_ref()) {
375
- res.headers_mut().insert(
376
- axum::http::header::ETAG,
377
- axum::http::HeaderValue::from_str(&stored_etag).unwrap(),
378
- );
500
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
501
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
502
+ {
503
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
379
504
  }
380
505
  res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("public, max-age=3600"));
381
506
  res
@@ -444,6 +569,8 @@ where
444
569
  .route("/items/{id}", get(items_get_item_handler::<S>))
445
570
  .route("/items", post(items_create_item_handler::<S>))
446
571
  .route("/items", put(items_update_item_handler::<S>))
572
+ .route("/accounts/{accountId}/images", get(images_list_public_handler::<S, C>))
573
+ .route("/accounts/{accountId}/images/{imageId}", get(images_get_image_handler::<S, C>))
447
574
  .route("/consuming", post(consuming_upload_handler::<S>))
448
575
  ;
449
576
  router = router.merge(public);
@@ -7,8 +7,9 @@ use axum::response::IntoResponse;
7
7
  use axum::response::sse::{Event, KeepAlive, Sse};
8
8
  use generated::server::{
9
9
  ArticlesGetArticleResponse, ConsumingConsumeAndDeleteResponse, ConsumingUploadResponse,
10
- EtagCache, EventsAccountsEventsResponse, ItemsCreateItemResponse, ItemsGetItemResponse,
11
- ItemsListResponse, ItemsUpdateItemResponse, PetsListResponse, Server,
10
+ EtagCache, EventsAccountsEventsResponse, ImagesGetImageResponse, ImagesListPublicResponse,
11
+ ItemsCreateItemResponse, ItemsGetItemResponse, ItemsListResponse, ItemsUpdateItemResponse,
12
+ PetsListResponse, Server,
12
13
  };
13
14
  use std::collections::HashMap;
14
15
  use std::convert::Infallible;
@@ -20,11 +21,12 @@ use tokio_stream::{wrappers::IntervalStream, StreamExt as _};
20
21
  #[derive(Clone, Default)]
21
22
  struct InMemoryCache(Arc<Mutex<HashMap<String, String>>>);
22
23
 
24
+ #[async_trait]
23
25
  impl EtagCache for InMemoryCache {
24
- fn get(&self, key: &str) -> Option<String> {
26
+ async fn get(&self, key: &str) -> Option<String> {
25
27
  self.0.lock().unwrap().get(key).cloned()
26
28
  }
27
- fn set(&self, key: &str, etag: &str) {
29
+ async fn set(&self, key: &str, etag: &str) {
28
30
  self.0.lock().unwrap().insert(key.to_owned(), etag.to_owned());
29
31
  }
30
32
  }
@@ -71,7 +73,7 @@ impl Server for AppState {
71
73
  &self,
72
74
  cache: &C,
73
75
  ) -> eyre::Result<ItemsListResponse> {
74
- cache.set("article-list", "\"items-etag\"");
76
+ cache.set("article-list", "\"items-etag\"").await;
75
77
  Ok(ItemsListResponse::Ok(Json(vec![generated::types::Item {
76
78
  name: "list-item".to_string(),
77
79
  value: 123,
@@ -106,6 +108,47 @@ impl Server for AppState {
106
108
  })))
107
109
  }
108
110
 
111
+ async fn images_list_public<C: EtagCache + Send + Sync>(
112
+ &self,
113
+ cache: &C,
114
+ account_id: uuid::Uuid,
115
+ ) -> eyre::Result<ImagesListPublicResponse> {
116
+ cache
117
+ .set(
118
+ &format!("img:list:pub:{}", account_id),
119
+ "\"images-list-etag\"",
120
+ )
121
+ .await;
122
+ Ok(ImagesListPublicResponse::Ok(Json(vec![
123
+ generated::types::Item {
124
+ name: "public-image-1".to_string(),
125
+ value: 200,
126
+ },
127
+ generated::types::Item {
128
+ name: "public-image-2".to_string(),
129
+ value: 201,
130
+ },
131
+ ])))
132
+ }
133
+
134
+ async fn images_get_image<C: EtagCache + Send + Sync>(
135
+ &self,
136
+ cache: &C,
137
+ account_id: uuid::Uuid,
138
+ image_id: String,
139
+ ) -> eyre::Result<ImagesGetImageResponse> {
140
+ cache
141
+ .set(
142
+ &format!("img:detail:pub:{}:{}", account_id, image_id),
143
+ "\"image-detail-etag\"",
144
+ )
145
+ .await;
146
+ Ok(ImagesGetImageResponse::Ok(Json(generated::types::Item {
147
+ name: format!("image-{}", image_id),
148
+ value: 300,
149
+ })))
150
+ }
151
+
109
152
  async fn articles_get_article<C: EtagCache + Send + Sync>(
110
153
  &self,
111
154
  _claims: Self::Claims,
@@ -113,7 +156,7 @@ impl Server for AppState {
113
156
  id: String,
114
157
  ) -> eyre::Result<ArticlesGetArticleResponse> {
115
158
  let etag = format!("\"article-etag-{}\"", id);
116
- cache.set(&format!("articles_get_article:{}", id), &etag);
159
+ cache.set(&format!("article-list:{}", id), &etag).await;
117
160
  Ok(ArticlesGetArticleResponse::Ok(Json(
118
161
  generated::types::Article {
119
162
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typespec-rust-emitter",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "TypeSpec emitter that generates idiomatic Rust types and structs",
5
5
  "keywords": [
6
6
  "typespec",
@@ -1,36 +1,36 @@
1
- import { readFileSync, writeFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { emit } from '../dist/test/test-host.js';
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { emit } from "../dist/test/test-host.js";
4
4
 
5
5
  async function main() {
6
6
  const feature = process.argv[2];
7
7
  if (!feature) {
8
- console.error('Usage: node scripts/update-golden.js <feature>');
8
+ console.error("Usage: node scripts/update-golden.js <feature>");
9
9
  process.exit(1);
10
10
  }
11
11
 
12
12
  const specPath = resolve(process.cwd(), `test/golden/${feature}/spec.tsp`);
13
- const spec = readFileSync(specPath, 'utf8');
14
-
13
+ const spec = readFileSync(specPath, "utf8");
14
+
15
15
  console.log(`Emitting spec for ${feature}...`);
16
16
  const out = await emit(spec);
17
17
 
18
- if (out['types.rs']) {
18
+ if (out["types.rs"]) {
19
19
  const p = resolve(process.cwd(), `test/golden/${feature}/types.rs`);
20
- writeFileSync(p, out['types.rs']);
20
+ writeFileSync(p, out["types.rs"]);
21
21
  console.log(`Updated types.rs`);
22
22
  }
23
-
24
- if (out['server.rs']) {
23
+
24
+ if (out["server.rs"]) {
25
25
  const p = resolve(process.cwd(), `test/golden/${feature}/server.rs`);
26
- writeFileSync(p, out['server.rs']);
26
+ writeFileSync(p, out["server.rs"]);
27
27
  console.log(`Updated server.rs`);
28
28
  }
29
29
 
30
30
  console.log(`Golden files updated for: ${feature}`);
31
31
  }
32
32
 
33
- main().catch(err => {
33
+ main().catch((err) => {
34
34
  console.error(err);
35
35
  process.exit(1);
36
36
  });
@@ -1,3 +1,5 @@
1
+ import type { ParameterInfo } from "../models/types.js";
2
+
1
3
  /**
2
4
  * generateEtagCacheTrait
3
5
  *
@@ -7,29 +9,73 @@
7
9
  export function generateEtagCacheTrait(): string {
8
10
  return `/// Pluggable ETag cache backend.
9
11
  /// Implement this for Redis, Memcached, in-memory HashMap, or any store.
12
+ #[async_trait]
10
13
  pub trait EtagCache {
11
14
  /// Return the stored ETag string for \`key\`, or \`None\` if not cached.
12
- fn get(&self, key: &str) -> Option<String>;
15
+ async fn get(&self, key: &str) -> Option<String>;
13
16
  /// Store \`etag\` under \`key\`.
14
- fn set(&self, key: &str, etag: &str);
17
+ async fn set(&self, key: &str, etag: &str);
15
18
  }
16
19
  `;
17
20
  }
18
21
 
22
+ function rustStringLiteral(value: string): string {
23
+ return `"${value
24
+ .replace(/\\/g, "\\\\")
25
+ .replace(/"/g, '\\"')
26
+ .replace(/\n/g, "\\n")
27
+ .replace(/\r/g, "\\r")
28
+ .replace(/\t/g, "\\t")}"`;
29
+ }
30
+
31
+ function buildCacheKeyExpression(opts: {
32
+ handlerFnName: string;
33
+ etagKey?: string;
34
+ pathParams: ParameterInfo[];
35
+ }): string {
36
+ const { handlerFnName, etagKey, pathParams } = opts;
37
+ const pathSuffix = pathParams.map((p) => `{${p.rustName}}`).join(":");
38
+ const paramNames = new Map<string, string>();
39
+ for (const param of pathParams) {
40
+ paramNames.set(param.name, param.rustName);
41
+ paramNames.set(param.rustName, param.rustName);
42
+ }
43
+
44
+ let template = etagKey ?? handlerFnName;
45
+ template = template.replace(/\{([^}]+)\}/g, (match, name: string) => {
46
+ const rustName = paramNames.get(name);
47
+ return rustName ? `{${rustName}}` : match;
48
+ });
49
+
50
+ const hasExplicitPathParam = pathParams.some(
51
+ (param) =>
52
+ template.includes(`{${param.name}}`) ||
53
+ template.includes(`{${param.rustName}}`),
54
+ );
55
+ if (pathSuffix && !hasExplicitPathParam) {
56
+ template = `${template}:${pathSuffix}`;
57
+ }
58
+
59
+ if (!template.includes("{")) {
60
+ return rustStringLiteral(template);
61
+ }
62
+ return `format!(${rustStringLiteral(template)})`;
63
+ }
64
+
19
65
  /**
20
66
  * generateEtagHandler
21
67
  *
22
68
  * Returns the Rust source for a single ETag-aware axum handler.
23
69
  *
24
70
  * @param handlerFnName snake_case name, e.g. "articles_get_article"
25
- * @param cacheKey format string for the cache key, e.g. "articles_get_article:{id}"
71
+ * @param pathParams route path parameters used for cache key scoping
26
72
  * @param extractorLines axum extractors needed by the operation
27
73
  * @param serverArgsStr comma-separated args forwarded to the trait method
28
74
  * @param responseName PascalCase name of the response enum, e.g. "ArticlesGetArticleResponse"
29
75
  */
30
76
  export function generateEtagHandler(opts: {
31
77
  handlerFnName: string;
32
- cacheKey: string;
78
+ pathParams: ParameterInfo[];
33
79
  extractorLines: string[];
34
80
  serverArgsStr: string;
35
81
  responseName: string;
@@ -39,7 +85,7 @@ export function generateEtagHandler(opts: {
39
85
  }): string {
40
86
  const {
41
87
  handlerFnName,
42
- cacheKey,
88
+ pathParams,
43
89
  extractorLines,
44
90
  serverArgsStr,
45
91
  etagKey,
@@ -47,18 +93,21 @@ export function generateEtagHandler(opts: {
47
93
  serviceBinding = "service",
48
94
  } = opts;
49
95
 
50
- const effectiveCacheKey = etagKey ? `"${etagKey}"` : `format!("${cacheKey}")`;
96
+ const effectiveCacheKey = buildCacheKeyExpression({
97
+ handlerFnName,
98
+ etagKey,
99
+ pathParams,
100
+ });
51
101
  const operationExtractors =
52
102
  extractorLines.length > 0 ? `${extractorLines.join("\n")}\n` : "";
53
103
 
54
104
  const finalResponseLogic = ` match result {
55
105
  Ok(response) => {
56
106
  let mut res = response.into_response();
57
- if let Some(stored_etag) = cache.get(cache_key.as_ref()) {
58
- res.headers_mut().insert(
59
- axum::http::header::ETAG,
60
- axum::http::HeaderValue::from_str(&stored_etag).unwrap(),
61
- );
107
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
108
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
109
+ {
110
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
62
111
  }
63
112
  ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
64
113
  res
@@ -82,13 +131,14 @@ where
82
131
  {
83
132
  // Check If-None-Match against the cache
84
133
  let cache_key = ${effectiveCacheKey};
85
- if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) = (cache.get(cache_key.as_ref()), if_none_match) {
86
- // If the client's ETag matches, respond 304 immediately
87
- if stored_etag == format!("{:?}", inm).trim_matches('"') {
88
- let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
89
- ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
90
- return res;
91
- }
134
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
135
+ (cache.get(cache_key.as_ref()).await, if_none_match)
136
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
137
+ && !inm.precondition_passes(&etag)
138
+ {
139
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
140
+ ${cacheControl ? `res.headers_mut().insert(axum::http::header::CACHE_CONTROL, axum::http::HeaderValue::from_static("${cacheControl}"));` : ""}
141
+ return res;
92
142
  }
93
143
  // Forward to business logic
94
144
  let result = ${serviceBinding}.${handlerFnName}(${serverArgsStr}).await;
@@ -117,13 +117,12 @@ export function generateRouter(
117
117
  const serverArgsStr = serverArgs.join(", ");
118
118
 
119
119
  if (hasEtagCache(program, op)) {
120
- const cacheKey = `${handlerFnName}:${pathParams.map((p) => `{${p.rustName}}`).join(":")}`;
121
120
  const cachedServerArgs = isProtected
122
121
  ? [serverArgs[0], "&cache", ...serverArgs.slice(1)]
123
122
  : ["&cache", ...serverArgs];
124
123
  const code = generateEtagHandler({
125
124
  handlerFnName,
126
- cacheKey,
125
+ pathParams,
127
126
  extractorLines,
128
127
  serverArgsStr: cachedServerArgs.join(", "),
129
128
  responseName: `${nsName}${toPascalCase(opInfo.name)}Response`,
@@ -144,7 +143,7 @@ export function generateRouter(
144
143
 
145
144
  const serverCall = `service.${traitFnName}(${serverArgsStr}).await`;
146
145
 
147
- let responseHandling = opInfo.cacheControl
146
+ const responseHandling = opInfo.cacheControl
148
147
  ? `match result {
149
148
  Ok(response) => {
150
149
  let mut res = response.into_response();
@@ -29,7 +29,11 @@ use eyre::Result;
29
29
 
30
30
  `);
31
31
 
32
- if (namespaceGroups.some((g) => g.operations.some((o) => hasEtagCache(program, o)))) {
32
+ if (
33
+ namespaceGroups.some((g) =>
34
+ g.operations.some((o) => hasEtagCache(program, o)),
35
+ )
36
+ ) {
33
37
  parts.push(generateEtagCacheTrait());
34
38
  parts.push("\n");
35
39
  }
@@ -128,7 +128,9 @@ export function emitOperationInfo(
128
128
  const opName = op.name.replace(/[^a-zA-Z0-9_]/g, "_");
129
129
  const etagVal = program.stateMap(etagCacheKey).get(op);
130
130
  const etagKey = typeof etagVal === "string" ? etagVal : undefined;
131
- const cacheControl = program.stateMap(cacheControlKey).get(op) as string | undefined;
131
+ const cacheControl = program.stateMap(cacheControlKey).get(op) as
132
+ | string
133
+ | undefined;
132
134
 
133
135
  return {
134
136
  name: opName,
@@ -144,7 +146,10 @@ export function emitOperationInfo(
144
146
  };
145
147
  }
146
148
 
147
- export function getEtagKey(program: Program, operation: Operation): string | undefined {
149
+ export function getEtagKey(
150
+ program: Program,
151
+ operation: Operation,
152
+ ): string | undefined {
148
153
  const val = program.stateMap(etagCacheKey).get(operation);
149
154
  return typeof val === "string" ? val : undefined;
150
155
  }
@@ -153,6 +158,9 @@ export function hasEtagCache(program: Program, operation: Operation): boolean {
153
158
  return program.stateMap(etagCacheKey).has(operation);
154
159
  }
155
160
 
156
- export function getCacheControl(program: Program, operation: Operation): string | undefined {
161
+ export function getCacheControl(
162
+ program: Program,
163
+ operation: Operation,
164
+ ): string | undefined {
157
165
  return program.stateMap(cacheControlKey).get(operation) as string | undefined;
158
166
  }
@@ -28,17 +28,29 @@ describe("@etagCache decorator", () => {
28
28
  const server = results["server.rs"];
29
29
  strictEqual(server.includes("pub trait EtagCache"), true);
30
30
  strictEqual(
31
- server.includes("fn get(&self, key: &str) -> Option<String>"),
31
+ server.includes("async fn get(&self, key: &str) -> Option<String>"),
32
+ true,
33
+ );
34
+ strictEqual(
35
+ server.includes("async fn set(&self, key: &str, etag: &str)"),
32
36
  true,
33
37
  );
34
- strictEqual(server.includes("fn set(&self, key: &str, etag: &str)"), true);
35
38
  });
36
39
 
37
40
  it("generates cache-aware handler with 304 short-circuit", async () => {
38
41
  const results = await emit(ETAG_SPEC);
39
42
  const server = results["server.rs"];
40
43
  strictEqual(server.includes("axum::http::StatusCode::NOT_MODIFIED"), true);
41
- strictEqual(server.includes("cache.get(cache_key.as_ref())"), true);
44
+ strictEqual(server.includes("cache.get(cache_key.as_ref()).await"), true);
45
+ strictEqual(
46
+ server.includes("stored_etag.parse::<axum_extra::headers::ETag>()"),
47
+ true,
48
+ );
49
+ strictEqual(server.includes("!inm.precondition_passes(&etag)"), true);
50
+ strictEqual(
51
+ server.includes("HeaderValue::from_str(&stored_etag).unwrap()"),
52
+ false,
53
+ );
42
54
  });
43
55
 
44
56
  it("supports custom etagKey and @cacheControl", async () => {
@@ -58,10 +70,52 @@ describe("@etagCache decorator", () => {
58
70
  const results = await emit(spec);
59
71
  const server = results["server.rs"];
60
72
  strictEqual(server.includes('let cache_key = "my-key";'), true);
61
- strictEqual(server.includes('axum::http::header::CACHE_CONTROL'), true);
73
+ strictEqual(server.includes("axum::http::header::CACHE_CONTROL"), true);
62
74
  strictEqual(server.includes('"public"'), true);
63
75
  });
64
76
 
77
+ it("interpolates path parameters in custom etag keys", async () => {
78
+ const spec = `
79
+ import "@typespec/http";
80
+ import "typespec-rust-emitter";
81
+ using TypeSpec.Http;
82
+
83
+ @route("/accounts/{accountId}/images")
84
+ namespace Images {
85
+ @etagCache("img:list:pub:{accountId}")
86
+ @get
87
+ op listPublic(@path accountId: string): { @statusCode statusCode: 200; };
88
+ }
89
+ `;
90
+ const results = await emit(spec);
91
+ const server = results["server.rs"];
92
+ strictEqual(
93
+ server.includes('let cache_key = format!("img:list:pub:{account_id}");'),
94
+ true,
95
+ );
96
+ });
97
+
98
+ it("appends path parameters to static etag keys", async () => {
99
+ const spec = `
100
+ import "@typespec/http";
101
+ import "typespec-rust-emitter";
102
+ using TypeSpec.Http;
103
+
104
+ @route("/accounts/{accountId}/images")
105
+ namespace Images {
106
+ @etagCache("img:list:pub")
107
+ @get
108
+ op listPublic(@path accountId: string): { @statusCode statusCode: 200; };
109
+ }
110
+ `;
111
+ const results = await emit(spec);
112
+ const server = results["server.rs"];
113
+ strictEqual(
114
+ server.includes('let cache_key = format!("img:list:pub:{account_id}");'),
115
+ true,
116
+ );
117
+ });
118
+
65
119
  it("orders cache after claims when combined with @useAuth", async () => {
66
120
  const spec = `
67
121
  import "@typespec/http";
@@ -12,11 +12,12 @@ use eyre::Result;
12
12
 
13
13
  /// Pluggable ETag cache backend.
14
14
  /// Implement this for Redis, Memcached, in-memory HashMap, or any store.
15
+ #[async_trait]
15
16
  pub trait EtagCache {
16
17
  /// Return the stored ETag string for `key`, or `None` if not cached.
17
- fn get(&self, key: &str) -> Option<String>;
18
+ async fn get(&self, key: &str) -> Option<String>;
18
19
  /// Store `etag` under `key`.
19
- fn set(&self, key: &str, etag: &str);
20
+ async fn set(&self, key: &str, etag: &str);
20
21
  }
21
22
 
22
23
 
@@ -64,24 +65,24 @@ where
64
65
  {
65
66
  // Check If-None-Match against the cache
66
67
  let cache_key = format!("articles_get_article:{id}");
67
- if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) = (cache.get(cache_key.as_ref()), if_none_match) {
68
- // If the client's ETag matches, respond 304 immediately
69
- if stored_etag == format!("{:?}", inm).trim_matches('"') {
70
- let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
71
-
72
- return res;
73
- }
68
+ if let (Some(stored_etag), Some(axum_extra::TypedHeader(inm))) =
69
+ (cache.get(cache_key.as_ref()).await, if_none_match)
70
+ && let Ok(etag) = stored_etag.parse::<axum_extra::headers::ETag>()
71
+ && !inm.precondition_passes(&etag)
72
+ {
73
+ let mut res = axum::http::StatusCode::NOT_MODIFIED.into_response();
74
+
75
+ return res;
74
76
  }
75
77
  // Forward to business logic
76
78
  let result = service.articles_get_article(&cache, id).await;
77
79
  match result {
78
80
  Ok(response) => {
79
81
  let mut res = response.into_response();
80
- if let Some(stored_etag) = cache.get(cache_key.as_ref()) {
81
- res.headers_mut().insert(
82
- axum::http::header::ETAG,
83
- axum::http::HeaderValue::from_str(&stored_etag).unwrap(),
84
- );
82
+ if let Some(stored_etag) = cache.get(cache_key.as_ref()).await
83
+ && let Ok(etag) = axum::http::HeaderValue::from_str(&stored_etag)
84
+ {
85
+ res.headers_mut().insert(axum::http::header::ETAG, etag);
85
86
  }
86
87
 
87
88
  res