Skip to content

[Rust] Apply the isAnyType param fallback to operations without path params - #24570

Open
emilbonnek wants to merge 1 commit into
OpenAPITools:masterfrom
emilbonnek:rust-anytype-query-params-without-path-params
Open

[Rust] Apply the isAnyType param fallback to operations without path params#24570
emilbonnek wants to merge 1 commit into
OpenAPITools:masterfrom
emilbonnek:rust-anytype-query-params-without-path-params

Conversation

@emilbonnek

@emilbonnek emilbonnek commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #20141

The rust client generates Option<models::models::TypeName> for an enum query parameter when the operation has no path parameters. That does not compile:

error[E0433]: cannot find `models` in `models`

The same parameter on an operation that does have a path parameter generates Option<&str> and is fine. That difference is the bug.

Cause

In RustClientCodegen.postProcessOperationsWithModels, the loop that defaults isAnyType path, query and header params to String (added in #20631) sits inside if (operation.pathParams.size() > 0). The loop iterates allParams and already guards on isPathParam || isQueryParam || isHeaderParam, so the outer condition only suppresses it for operations without path params.

When it is suppressed, the param keeps a dataType that already carries the models:: prefix from AbstractRustCodegen.getTypeDeclaration, and api.mustache prefixes it again.

This only shows up when the parameter schema is a $ref the parser does not treat as a bare ref, so it is not marked isString: a $ref wrapped in anyOf with null, or a $ref with sibling keywords. Both are common in FastAPI output. A bare $ref was already fine, which is why the existing enum-query-params sample did not catch it.

The fix moves the loop out of the pathParams check, which is what #20631 describes doing.

Reproducing

{
  "openapi": "3.1.0",
  "info": { "title": "Repro", "version": "1.0.0" },
  "paths": {
    "/widgets/summary": {
      "get": {
        "operationId": "getWidgetSummary",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [ { "$ref": "#/components/schemas/WidgetKind" }, { "type": "null" } ],
              "title": "Kind"
            }
          }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WidgetSummary" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WidgetKind": { "type": "string", "enum": [ "basic", "premium" ], "title": "WidgetKind" },
      "WidgetSummary": {
        "type": "object",
        "title": "WidgetSummary",
        "properties": { "kind": { "$ref": "#/components/schemas/WidgetKind" }, "total": { "type": "integer" } },
        "required": [ "kind", "total" ]
      }
    }
  }
}
openapi-generator-cli generate -i repro.json -g rust --skip-validate-spec

Before: kind: Option<models::models::WidgetKind>, cargo build fails.
After: kind: Option<&str>, cargo build passes.

Testing

  • Regenerated all 37 rust configs. No existing sample changed, so no committed output regresses.
  • Added rust-reqwest-nullable-enum-query-params, a 3.1 spec covering both shapes with and without a path parameter. It does not compile on master and does compile with this change.
  • Rust*Test passes, 30 tests.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in Git BASH)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

cc: @frol @farcaller @richardwhiuk @paladinzh @jacob-pro


Summary by cubic

Fixes a Rust codegen bug where enum query params in operations without path params compiled as models::models::TypeName. We now always default isAnyType path/query/header params to String, so generated clients compile consistently.

  • Bug Fixes
    • Apply the isAnyType fallback for path/query/header params on all operations (moved loop outside the path params check).
    • Prevent double models:: prefix; affected params now emit as strings instead of models::TypeName.
    • Added a 3.1 spec and reqwest sample covering nullable enum query params (with and without path params); regenerated Rust configs (no changes) and all Rust*Test tests pass.

Written for commit 23b0ef9. Summary will update on new commits.

Review in cubic

…params

The block that defaults isAnyType path, query and header params to String
was nested inside a check for the operation having path params, so it never
ran for operations that have none. Those params kept a dataType that already
carries the models:: prefix, and the api template prefixes it again, giving
models::models::TypeName which does not compile.

Fixes OpenAPITools#20141

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java:781">
P2: JSON-content query parameters with an unconstrained schema now become `Option<&str>` rather than `Option<serde_json::Value>` when the operation has no path params. Preserve `serde_json::Value` for `queryIsJsonMimeType` parameters so callers can still supply and serialize arbitrary JSON values.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// However for path, query, and headers it's unlikely to be JSON so we default to `String`.
// Note that we keep the default `serde_json::Value` for body parameters.
for (var param : operation.allParams) {
if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: JSON-content query parameters with an unconstrained schema now become Option<&str> rather than Option<serde_json::Value> when the operation has no path params. Preserve serde_json::Value for queryIsJsonMimeType parameters so callers can still supply and serialize arbitrary JSON values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java, line 781:

<comment>JSON-content query parameters with an unconstrained schema now become `Option<&str>` rather than `Option<serde_json::Value>` when the operation has no path params. Preserve `serde_json::Value` for `queryIsJsonMimeType` parameters so callers can still supply and serialize arbitrary JSON values.</comment>

<file context>
@@ -774,19 +774,18 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
+            // However for path, query, and headers it's unlikely to be JSON so we default to `String`.
+            // Note that we keep the default `serde_json::Value` for body parameters.
+            for (var param : operation.allParams) {
+                if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {
+                    param.dataType = "String";
+                    param.isPrimitiveType = true;
</file context>
Suggested change
if (param.isAnyType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {
if (param.isAnyType && !param.queryIsJsonMimeType && (param.isPathParam || param.isQueryParam || param.isHeaderParam)) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][Rust] Bad module import generated with Option types

1 participant