Skip to content

Commit 92d5b73

Browse files
wiebrenclaude
andauthored
[rust] fix: map-typed query parameters do not compile (.to_string() on a HashMap) (#24896)
* [rust] fix: map-typed query parameters called .to_string() on a HashMap HashMap implements neither Display nor ToString, so the generated client did not compile (E0599) wherever a map-typed query parameter fell through to the scalar path: a required non-nullable map in both reqwest and reqwest-trait, and the nullable/optional non-deepObject fallthroughs in reqwest-trait. Serialize the map as one json-encoded parameter with serde_json::to_string, exactly how reqwest's optional branch already handles non-primitive parameters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz * [rust] test: pin the required map-query assertion to its parameter `serde_json::to_string(&` matched any call in the file; match on the `labels` key instead, which both libraries emit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 64d94d3 commit 92d5b73

4 files changed

Lines changed: 69 additions & 0 deletions

File tree

‎modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,12 @@ impl {{classname}} for {{classname}}Client {
188188
{{/isArray}}
189189
{{^isArray}}
190190
{{^isNullable}}
191+
{{#isMap}}
192+
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &serde_json::to_string(&{{{paramName}}})?)]);
193+
{{/isMap}}
194+
{{^isMap}}
191195
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.to_string())]);
196+
{{/isMap}}
192197
{{/isNullable}}
193198
{{#isNullable}}
194199
{{#isDeepObject}}
@@ -225,9 +230,16 @@ impl {{classname}} for {{classname}}Client {
225230
{{/isModel}}
226231
{{^isObject}}
227232
{{^isModel}}
233+
{{#isMap}}
234+
if let Some(ref param_value) = {{{paramName}}} {
235+
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &serde_json::to_string(param_value)?)]);
236+
};
237+
{{/isMap}}
238+
{{^isMap}}
228239
if let Some(ref param_value) = {{{paramName}}} {
229240
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &param_value.to_string())]);
230241
};
242+
{{/isMap}}
231243
{{/isModel}}
232244
{{/isObject}}
233245
{{/isDeepObject}}
@@ -267,7 +279,12 @@ impl {{classname}} for {{classname}}Client {
267279
{{/isModel}}
268280
{{^isObject}}
269281
{{^isModel}}
282+
{{#isMap}}
283+
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &serde_json::to_string(param_value)?)]);
284+
{{/isMap}}
285+
{{^isMap}}
270286
local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &param_value.to_string())]);
287+
{{/isMap}}
271288
{{/isModel}}
272289
{{/isObject}}
273290
{{/isDeepObject}}

‎modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,12 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration:
166166
{{/isArray}}
167167
{{^isArray}}
168168
{{^isNullable}}
169+
{{#isMap}}
170+
req_builder = req_builder.query(&[("{{{baseName}}}", &serde_json::to_string(&{{{vendorExtensions.x-rust-param-identifier}}})?)]);
171+
{{/isMap}}
172+
{{^isMap}}
169173
req_builder = req_builder.query(&[("{{{baseName}}}", &{{{vendorExtensions.x-rust-param-identifier}}}.to_string())]);
174+
{{/isMap}}
170175
{{/isNullable}}
171176
{{#isNullable}}
172177
{{#isDeepObject}}

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,4 +449,26 @@ private Path generateReqwestConfiguration(String library, boolean supportTokenSo
449449
TestUtils.assertFileExists(outputPath);
450450
return outputPath;
451451
}
452+
453+
@Test
454+
public void testMapQueryParamsSerializeAsJson() throws IOException {
455+
// HashMap has no Display: map-typed query params must serialize via serde_json::to_string
456+
for (String library : new String[] {"reqwest", "reqwest-trait"}) {
457+
Path target = Files.createTempDirectory("test");
458+
target.toFile().deleteOnExit();
459+
final CodegenConfigurator configurator = new CodegenConfigurator()
460+
.setGeneratorName("rust")
461+
.setLibrary(library)
462+
.setInputSpec("src/test/resources/3_0/rust/map-query-params.yaml")
463+
.setSkipOverwrite(false)
464+
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
465+
new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
466+
Path outputPath = Path.of(target.toString(), "/src/apis/default_api.rs");
467+
TestUtils.assertFileExists(outputPath);
468+
TestUtils.assertFileContains(outputPath, "(\"labels\", &serde_json::to_string(&");
469+
TestUtils.assertFileContains(outputPath, "(\"counts\", &serde_json::to_string(param_value)?)");
470+
TestUtils.assertFileNotContains(outputPath, "labels.to_string()");
471+
TestUtils.assertFileNotContains(outputPath, "param_value.to_string()");
472+
}
473+
}
452474
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
openapi: 3.0.3
2+
info:
3+
title: map query params
4+
version: 1.0.0
5+
paths:
6+
/items:
7+
get:
8+
operationId: listItems
9+
parameters:
10+
- name: labels
11+
in: query
12+
required: true
13+
schema:
14+
type: object
15+
additionalProperties:
16+
type: string
17+
- name: counts
18+
in: query
19+
schema:
20+
type: object
21+
additionalProperties:
22+
type: integer
23+
responses:
24+
'200':
25+
description: ok

0 commit comments

Comments
 (0)