[rust-axum] fix: let the implementation set the status of default responses

TE
16h ago
+2511-30

Fixes #25283
Related: #25294 changes the same rust-axum response templates (range status codes); whichever lands second needs a rebase.

A default response generated a variant with no status code, and the handler called Response::builder().status(0). With a body, the following headers_mut().unwrap() panicked; without one, the client got a 500. So default responses could not be returned at all, including the petstore user operations whose only response is default.

Changes

  • response.mustache: a default response variant now carries a u16 status code chosen by the implementation:
    • Status0_UnexpectedError(u16) when there is no body;
    • Status0_UnexpectedError(u16, models::Error) with a body (same for plain text, bytes and SSE bodies);
    • a status: u16 field when the variant is a struct because of response headers.
  • server-operation.mustache: destructure that status, validate it with StatusCode::from_u16 and use it instead of 0. An invalid code is logged (with the rejected value) and answered with 500, like other serialization errors in the handler. This applies to SSE bodies too.
  • Responses with an explicit status code are unchanged.

This changes the shape of default variants. Code that constructs them stops compiling. Such code could not send a correct response before (it either panicked or returned 500).

Not covered

  • Range responses (2XX, ...) still go through the existing {{#range}} branch, which is unchanged.
  • rust-server has a similar issue (StatusCode::from_u16(0).expect(..)). Left for a separate PR.

Testing

  • New RustAxumServerCodegenTest#testDefaultResponseCarriesStatus with src/test/resources/3_0/rust-axum/rust-axum-default-response.yaml. The spec has default responses with a JSON body, with no body, with a body and a header, with a header and no body, and with an SSE body. The test fails without the template change.
  • New sample rust-axum-default-response (config bin/configs/manual/rust-axum-default-response.yaml). It builds with --all-features and --no-default-features without warnings, and the petstore sample tests pass.
  • I drove the generated router with tower::ServiceExt::oneshot. Status codes chosen by the implementation (503/404/418/409, and 202 for SSE) come back with the expected headers and bodies, and an invalid code (1000) gives 500, SSE included. On master the same calls panic when there is a body, return 500 when there is none, and always return 200 for SSE.

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
    
    Only the rust-axum samples were regenerated: RUST_POST_PROCESS_FILE=$PWD/samples/server/petstore/rust-axum/rustfmt-2021.sh ./bin/generate-samples.sh bin/configs/manual/rust-axum*. That covers all rust-axum configs plus the new rust-axum-default-response sample. The change is template-only and limited to rust-axum. No generator options changed, so the docs are unaffected.
    Commit all changed files.
  • 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 default responses no longer panic or return 500: the generated server now requires implementation-provided status codes for default response variants. Invalid codes are logged and answered with 500, SSE bodies included.

Breaking change

  • Default response variants now include a u16 status code that the implementation must supply; existing code constructing these variants will not compile.