diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache index cf16c0fc24b2..093832d31435 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache @@ -205,12 +205,11 @@ impl {{classname}} for {{classname}}Client { } {{/isModel}} {{#isMap}} + {{!-- one route for the HashMap and the free-form serde_json::Value shape alike, + and the same deepObject wire format the non-explode branch produces --}} if let Some(ref param_value) = {{{paramName}}} { - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - local_var_req_builder = local_var_req_builder.query(&query_params); + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + local_var_req_builder = local_var_req_builder.query(¶ms); } {{/isMap}} {{/isExplode}} @@ -256,11 +255,10 @@ impl {{classname}} for {{classname}}Client { local_var_req_builder = local_var_req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - local_var_req_builder = local_var_req_builder.query(&query_params); + {{!-- one route for the HashMap and the free-form serde_json::Value shape alike, + and the same deepObject wire format the non-explode branch produces --}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + local_var_req_builder = local_var_req_builder.query(¶ms); {{/isMap}} {{/isExplode}} {{/isDeepObject}} diff --git a/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache b/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache index 5224b0c4d9b5..8b363e700593 100644 --- a/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache +++ b/modules/openapi-generator/src/main/resources/rust/reqwest/api.mustache @@ -180,11 +180,10 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: req_builder = req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - req_builder = req_builder.query(&query_params); + {{!-- one route for the HashMap and the free-form serde_json::Value shape alike, + and the same deepObject wire format the non-explode branch produces --}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); {{/isMap}} {{/isExplode}} }; @@ -251,11 +250,10 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: req_builder = req_builder.query(¶m_value); {{/isModel}} {{#isMap}} - let mut query_params = Vec::with_capacity(param_value.len()); - for (key, value) in param_value.iter() { - query_params.push((key.to_string(), serde_json::to_string(value)?)); - } - req_builder = req_builder.query(&query_params); + {{!-- one route for the HashMap and the free-form serde_json::Value shape alike, + and the same deepObject wire format the non-explode branch produces --}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", &serde_json::to_value(param_value)?); + req_builder = req_builder.query(¶ms); {{/isMap}} {{/isExplode}} {{/isDeepObject}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java index d4bc7c1845ec..bebb54132710 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java @@ -293,6 +293,36 @@ public void testIntegerPropertyEnum() throws IOException { TestUtils.assertFileNotContains(outputPath, linearize("#[serde(rename = \"0\")]")); } + @Test + public void testDeepObjectFreeFormQueryParamCompiles() throws IOException { + // the exploded deepObject branch walked every map-flagged parameter with + // .len()/.iter() - fine for a HashMap-typed map, but a bare free-form object is a + // serde_json::Value, which has neither, so the generated crate did not compile; + // both shapes now take parse_deep_object, the route the non-explode branch already + // uses, which also yields the deepObject wire format the style asks for + for (String library : new String[] {"reqwest", "reqwest-trait"}) { + Path target = Files.createTempDirectory("test"); + target.toFile().deleteOnExit(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("rust") + .setLibrary(library) + .setInputSpec("src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString().replace("\\", "/")); + List files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + Path outputPath = Path.of(target.toString(), "/src/apis/default_api.rs"); + TestUtils.assertFileExists(outputPath); + // the optional typed map, the optional free-form object, and the + // required-nullable map all route through parse_deep_object + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"filter\""); + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"extra\""); + TestUtils.assertFileContains(outputPath, "crate::apis::parse_deep_object(\"scope\""); + TestUtils.assertFileNotContains(outputPath, "param_value.len()"); + TestUtils.assertFileNotContains(outputPath, "param_value.iter()"); + } + } + @Test public void testArrayWithObjectEnumValues() throws IOException { Path target = Files.createTempDirectory("test"); diff --git a/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml b/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml new file mode 100644 index 000000000000..5780ef2bf2da --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/rust/deep-object-free-form-query-param.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +info: + title: deep object params + version: 1.0.0 +paths: + /items: + get: + operationId: listItems + parameters: + - name: filter + in: query + style: deepObject + explode: true + schema: + type: object + additionalProperties: true + - name: extra + in: query + style: deepObject + explode: true + schema: + type: object + - name: scope + in: query + required: true + style: deepObject + explode: true + schema: + type: object + nullable: true + additionalProperties: + type: string + responses: + '200': + description: ok