[rust-axum] fix: support range response status codes (4XX, 5XX)

TE
16h ago
+2410-1

Fixes #25293
Related: #25284 changes the same rust-axum response templates (default responses); whichever lands second needs a rebase.

Range responses (4XX, 5XX, ...) generated code that did not compile. The server template rendered response.status::<u16>(body.code.parse().unwrap()), which assumes the body has a code field (E0609), breaks when the response has no body (E0425), and drops the builder returned by status() (E0382). The response enum also gave the implementation no way to choose the concrete status.

Changes:

  • response.mustache: a range response is always generated as a struct variant with a status: u16 field, followed by body (if any) and the response headers, e.g. Status4XX_ClientError { status: u16, body: models::Error }. u16 is used (rather than http::StatusCode) because the response enums derive Serialize/Deserialize.
  • server-operation.mustache: range variants are destructured with status. The status is converted with StatusCode::from_u16 before the response is built (let mut response = response.status(status);), and the SSE path sets parts.status the same way. A status outside 100-999 returns 500 instead of panicking or falling back to 200.
  • Non-range responses render exactly as before: no existing rust-axum sample changes.

Not covered: the generated server does not check that status actually falls inside the declared range (e.g. returning 200 from a 4XX variant is not rejected). rust-server has the same gap and is not touched here.

Testing:

  • New RustAxumServerCodegenTest#testRangeResponseCodes with 3_0/rust-axum/rust-axum-range-responses.yaml (range responses with a body, without a body, with required and optional headers, and an SSE 2XX range response).
  • New sample rust-axum-range-responses (bin/configs/manual/rust-axum-range-responses.yaml). None of the existing rust-axum-only specs fit, and the shared rust-server specs cannot take range codes without breaking rust-server. Verified locally with cargo build --all-targets --all-features, cargo build --all-targets --no-default-features, cargo test, cargo clippy and cargo doc.
  • Also checked, by calling the generated router, that an out-of-range status (e.g. 1000) returns 500 on the JSON, empty-body and SSE paths.

PR checklist

  • Read the contribution guidelines.
  • 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 WSL)
    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.
    Regenerated only the rust-axum samples: RUST_POST_PROCESS_FILE=$PWD/samples/server/petstore/rust-axum/rustfmt-2021.sh ./bin/generate-samples.sh bin/configs/manual/rust-axum* (all 17 configs, including the new one). The only change is the new sample; no generator options changed, so the docs are unaffected.
  • 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 @dsteeley


Summary by cubic

Fixes the rust-axum generator so range responses (4XX, 5XX) generate code that compiles. Previously, the generated server referenced a body.code field that may not exist, broke when the response had no body, and discarded the builder returned by status().

Range response variants now expose a status: u16 field so implementations can choose the concrete status code, and the server builds the response with response.status(status). An invalid status code now returns 500 instead of panicking or falling back to 200. Adds a new codegen test and sample covering range responses with and without bodies and headers.