Skip to content

[rust-salvo] Add Rust server generator targeting the Salvo web framework#23772

Open
AuroraMaster wants to merge 10 commits into
OpenAPITools:masterfrom
AuroraMaster:add-rust-salvo-server-generator
Open

[rust-salvo] Add Rust server generator targeting the Salvo web framework#23772
AuroraMaster wants to merge 10 commits into
OpenAPITools:masterfrom
AuroraMaster:add-rust-salvo-server-generator

Conversation

@AuroraMaster
Copy link
Copy Markdown

@AuroraMaster AuroraMaster commented May 12, 2026

What this PR does

Adds a new rust-salvo server generator that mirrors the rust-axum baseline (security, schema composition, parameter / wire format coverage) but targets the Salvo web framework.

Closes #23771.

Highlights

  • Handlers use #[salvo::oapi::endpoint] plus the typed extractors from salvo::oapi::extract (JsonBody<T>, PathParam<T>, QueryParam<T, REQUIRED>, HeaderParam<T, REQUIRED>, FormBody<T>).
  • Models derive salvo::oapi::ToSchema so they flow through the extractors and surface in the generated OpenAPI doc.
  • Routes use the modern {name} path syntax (Salvo >= 0.76), driven by a salvoRoutes bundle populated in postProcessSupportingFileData.
  • Auth middleware (ApiKeyAuth / BasicAuth / BearerAuth) is scaffolded from the spec's securitySchemes when enableAuthMiddleware=true. CORS and request validation are gated by enableCorsMiddleware / enableRequestValidation.
  • Cargo deps pinned to current stable: salvo 0.93, validator 0.20, base64 0.22.

Validation

  • RustSalvoServerCodegenTest: 7/7 pass.
  • Generated petstore (bin/configs/manual/rust-salvo-petstore.yaml) and all three test specs compile with cargo check cleanly on Salvo 0.93 (0 errors, 0 warnings).
  • docs/generators/rust-salvo.md auto-exported via bin/utils/export_generator.sh; declared feature set matches rust-axum (allOf / anyOf / oneOf, JSON, ApiKey + Basic + Bearer).
  • Forbiddenapis check passes locally on modules/openapi-generator.

cc Rust technical committee: @frol @farcaller @richardwhiuk @paladinzh @jacob-pro @dsteeley

PR checklist


Summary by cubic

Adds a new rust-salvo server generator targeting Salvo 0.93 with near-parity to rust-axum. It scaffolds per-tag handlers, models, routes, and optional auth/CORS/validation, plus docs, tests, and a petstore sample that builds cleanly.

  • New Features

    • Generates handlers (grouped by tag), models, routes, and optional middleware for Salvo 0.93.
    • Uses #[salvo::oapi::endpoint] with typed extractors: JsonBody<T>, PathParam<T>, QueryParam<T, REQUIRED>, HeaderParam<T, REQUIRED>, FormBody<T>.
    • Models derive salvo::oapi::ToSchema; fields add #[serde(rename = "...")] to preserve wire names.
    • Modern route paths keep {name} syntax; auth attached per operation via route .hoop(...).
    • Endpoint security emits one entry per scheme (OR semantics) and preserves OAuth2 scopes; Basic and Bearer split into separate middleware types/factories with case-insensitive Authorization parsing.
    • Flags: enableAuthMiddleware, enableCorsMiddleware, enableRequestValidation, enableResponseValidation; supports JSON, ApiKey/Basic/Bearer auth, and allOf/anyOf/oneOf.
    • Registered in SPI; docs added (docs/generators/rust-salvo.md); tests cover basic/auth/validation; petstore sample passes cargo check; CI adds a dedicated rust-salvo build job.
  • Dependencies

    • salvo 0.93 (features: oapi, cors, jwt-auth, serve-static).
    • validator 0.20 (with request validation), base64 0.22, tokio 1, async-trait, chrono, serde, serde_json, tracing, tracing-subscriber.
    • Optional uuid 1 when UUIDs are used.

Written for commit e488d1f. Summary will update on new commits.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

6 issues found across 34 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/resources/rust-salvo/handlers.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/rust-salvo/handlers.mustache:30">
P2: Multiple auth schemes are collapsed into one security requirement, so alternative auth methods become combined (AND) instead of remaining alternatives (OR).</violation>
</file>

<file name="modules/openapi-generator/src/test/resources/3_0/rust/rust-salvo-auth-test.yaml">

<violation number="1" location="modules/openapi-generator/src/test/resources/3_0/rust/rust-salvo-auth-test.yaml:64">
P2: Auth fixture omits BasicAuth, so the rust-salvo middleware test never exercises the basic-auth path it expects.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/rust-salvo/lib.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/rust-salvo/lib.mustache:48">
P1: Auth is installed as a global service hoop, so it runs on every request and can block public operations because the generated middleware is not operation-aware.</violation>
</file>

<file name="samples/server/petstore/rust-salvo/output/petstore/src/models.rs">

<violation number="1" location="samples/server/petstore/rust-salvo/output/petstore/src/models.rs:72">
P1: Missing serde rename attributes cause JSON wire names to drift from OpenAPI camelCase to Rust snake_case, breaking API contract interoperability. For example, `pet_id` serializes as `pet_id` instead of `petId`, `ship_date` instead of `shipDate`, and `photo_urls` instead of `photoUrls`. The codegen templates should emit `#[serde(rename = "...")]` (or a struct-level `#[serde(rename_all = "camelCase")]` if all fields follow that convention) to preserve the original OpenAPI field names.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/rust-salvo/middleware.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/rust-salvo/middleware.mustache:78">
P2: Authentication scheme parsing is case-sensitive, so valid Basic/Bearer `Authorization` headers with different casing will be rejected.</violation>
</file>

<file name="samples/server/petstore/rust-salvo/output/petstore/README.md">

<violation number="1" location="samples/server/petstore/rust-salvo/output/petstore/README.md:65">
P3: The generated README leaves the API endpoints section empty instead of listing the server routes.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Comment thread modules/openapi-generator/src/main/resources/rust-salvo/lib.mustache Outdated
Comment thread samples/server/petstore/rust-salvo/output/petstore/src/models.rs
Comment thread modules/openapi-generator/src/main/resources/rust-salvo/handlers.mustache Outdated
Comment thread samples/server/petstore/rust-salvo/output/petstore/README.md
@@ -0,0 +1,11 @@
generatorName: rust-salvo
outputDir: samples/server/petstore/rust-salvo/output/petstore
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please add the new sample folder to the workflow: https://github.com/OpenAPITools/openapi-generator/blob/master/.github/workflows/samples-rust-server.yaml so that CI will test it moving forward

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

1 issue found across 20 files (changes from recent commits).

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="samples/server/petstore/rust-salvo/output/petstore/src/handlers/pet.rs">

<violation number="1" location="samples/server/petstore/rust-salvo/output/petstore/src/handlers/pet.rs:27">
P1: OAuth2 scope requirements are dropped in generated Salvo endpoint annotations, using empty scope arrays instead of the scopes defined in the source OpenAPI spec.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread samples/server/petstore/rust-salvo/output/petstore/src/handlers/pet.rs Outdated
@wing328
Copy link
Copy Markdown
Member

wing328 commented May 13, 2026

Thanks for the PR but your commit (as shown in the Commits tab) is not linked to your Github account, which means this PR won't count as your contribution in https://github.com/OpenAPITools/openapi-generator/graphs/contributors.

Let me know if you need help fixing it.

Ref: https://github.com/OpenAPITools/openapi-generator/wiki/FAQ#how-can-i-update-commits-that-are-not-linked-to-my-github-account

- Add RustSalvoServerCodegen.java with traditional MVC architecture
- Support for Salvo web framework with async handlers
- Include request validation, auth middleware, and CORS support
- Generate handlers, models, routes, and middleware modules
- Follow OpenAPI Generator conventions for consistency
- Register RustSalvoServerCodegen in SPI configuration
- Add RustSalvoServerCodegenTest with comprehensive test cases
- Create test OpenAPI specs for basic, auth, and validation scenarios
- Test file generation, dependency injection, and middleware support
- Verify Cargo.toml, handlers, routes, and validation features
Bring rust-salvo up to current Salvo idioms after the long rebase against
upstream master. The generator was previously pinned to Salvo 0.73 with
deprecated `<>` path syntax, a non-existent `salvo-cors` crate, a broken
`lambdaVersion` lambda reference, and a base64 0.21 API that no longer
compiles on the 0.22 line.

Highlights:
- Cargo.mustache: salvo 0.73 -> 0.93, validator 0.18 -> 0.20, drop the
  fake `salvo-cors` dependency (cors is a salvo feature), pin tokio to
  the macros/rt-multi-thread/signal subset, add base64/async-trait.
- Codegen: stop converting OpenAPI `{id}` paths to the deprecated `<id>`
  syntax; Salvo 0.76+ matches OpenAPI directly. Push boolean option
  defaults into additionalProperties so templates and tests can rely on
  them.
- handlers.mustache: rewrite to use `salvo::oapi::extract::{JsonBody,
  PathParam, QueryParam, HeaderParam, FormBody}` instead of mixing the
  oapi endpoint macro with the legacy `#[salvo(extract(...))]` attribute.
  Triple-stash `dataType` so generics like `Vec<String>` aren't
  HTML-escaped.
- handlers-mod.mustache: iterate `apis` by tag (one `pub mod` per API
  group) instead of iterating operations, which produced duplicates.
- middleware.mustache: switch base64 to the 0.22 `Engine` API and stop
  bubbling a `Result` out of a `()`-returning handler.
- models.mustache: derive `salvo::oapi::ToSchema` so generated models can
  flow through `JsonBody<T>` and friends. Triple-stash `dataType`.
- Add `bin/configs/manual/rust-salvo-petstore.yaml` so samples can be
  regenerated through the standard flow.
- Tests: align fixtures with the new output (salvo 0.93, validator 0.20,
  modern auth struct names, per-tag handler module names).

Known follow-ups: a full petstore generation still has ~21 remaining
`cargo check` errors around inline-enum duplication and ToSchema
coverage; those require model-template work to reach axum-level parity.
- models.mustache: drop the inline-enum block that emitted empty
  `pub enum Status {}` for each model with an enum-valued field. The
  variant set was never populated (the codegen still types these as
  `Option<String>`), so the empty enums only produced E0428 duplicate
  definitions across models that happened to share a field name.
- models.mustache: `use crate::models;` so `models::Foo` cross-references
  emitted by the codegen resolve while we are already inside the models
  module (same trick rust-axum uses).
- handlers.mustache: replace `use crate::models::*;` with
  `use crate::models::{self, *};` so handler files also see the `models`
  module name (needed for `JsonBody<Vec<models::User>>` and similar).
- models.mustache: merge the two `#[derive(...)]` lines, remove now-dead
  `hasEnums` block.

Verified with the bundled petstore.yaml plus all three rust-salvo test
specs — every generated project passes `cargo check` cleanly against
salvo 0.93. The Java test suite (7 tests) still passes.
- RustSalvoServerCodegen: claim allOf and anyOf alongside oneOf in
  schemaSupportFeatures so the generator advertises the same composite
  schema surface as rust-axum (now ✓ in the feature matrix).
- Add docs/generators/rust-salvo.md (auto-generated via
  bin/utils/export_generator.sh), matching the structure of
  rust-axum.md / rust-server.md.
- README.mustache: rewrite to reflect what the generator actually emits
  on Salvo 0.93 — the salvo::oapi::endpoint macro, the typed extractors
  from salvo::oapi::extract, ToSchema-derived models, the routes /
  handlers / middleware module layout, and re-generation flags for
  optional auth / CORS / validation. Drops stale wording about Salvo
  1.70 and the previous middleware shape.
- CLAUDE.md: replace the "in-progress" stub with an accurate snapshot
  of the rust-salvo generator's HEAD state and remaining axum-parity
  gaps.
CI build failed forbiddenapis check at RustSalvoServerCodegen.java:332
and :337 because String#toLowerCase() relies on the JVM default locale
and can produce wrong results under, e.g., the Turkish locale. Switch
to toLowerCase(Locale.ROOT) and lift the value into a local so both
call sites share the locale-safe form.

Local `./mvnw verify` on modules/openapi-generator now passes the
forbiddenapis scan with 0 errors, and the 7 RustSalvoServerCodegenTest
cases still pass.
CLAUDE.md is fork-local development guidance (kept on the fork's
master) and should not be part of the upstream PR.
Output of bin/configs/manual/rust-salvo-petstore.yaml. Verified
with cargo check (clean) against Salvo 0.93.
- Attach authentication as per-operation `.hoop(...)` on the route rather
  than a global service hoop, so public operations are no longer blocked
  by the generated middleware.
- Emit one `security(("<scheme>" = []))` entry per scheme on
  `#[salvo::oapi::endpoint]`, matching OpenAPI OR-of-requirements
  semantics instead of collapsing alternatives into AND.
- Add `#[serde(rename = "<wire>")]` to model fields whose Rust snake_case
  name diverges from the OpenAPI baseName so the JSON contract is
  preserved (petId, photoUrls, shipDate, …).
- Make `Authorization` scheme prefix matching case-insensitive in the
  Basic/Bearer middleware via a shared helper.
- Split the HTTP-scheme middleware emission into separate
  `isBasicBasic` / `isBasicBearer` paths so both BasicAuth and BearerAuth
  structs are generated, with matching per-route factory functions.
- Backfill the auth test fixture with BasicAuth and a multi-scheme (OR)
  operation; tighten the codegen test to cover the new factories,
  per-route hoops, and serde renames.
- Render the README's `## API endpoints` section from `apiInfo.apis`.
- Add a dedicated `build-salvo` job to `samples-rust-server.yaml` so the
  rust-salvo sample is covered in CI alongside rust-server / rust-axum.
@AuroraMaster AuroraMaster force-pushed the add-rust-salvo-server-generator branch from f3a99bb to 77df781 Compare May 13, 2026 11:38
@AuroraMaster
Copy link
Copy Markdown
Author

Thanks for flagging this — rebased the branch to attribute all commits to my GitHub noreply email (45430047+AuroraMaster@users.noreply.github.com). The Commits tab should now show them linked to my account. Also wired the new samples/server/petstore/rust-salvo/ into samples-rust-server.yaml (separate build-salvo job since it ships as a single crate rather than the output/* layout used by rust-server/rust-axum).

Pull the declared scopes from `CodegenSecurity.scopes` onto each Salvo
auth scheme descriptor and render them inside the
`#[salvo::oapi::endpoint(security(...))]` array. Previously every
scheme was emitted with an empty `[]`, dropping the OAuth2 / OIDC
authorization scopes from the OpenAPI source spec
(e.g. `("OAuth2" = [])` → `("OAuth2" = ["write:pets", "read:pets"])`).
# these folders contain sub-projects of rust clients, servers
- samples/server/petstore/rust-server/
- samples/server/petstore/rust-server-deprecated/
- samples/server/petstore/rust-axum/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i think we need to add the new folder here as well

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REQ] [rust-salvo] New server generator targeting the Salvo web framework

2 participants