From 98270980579564d01ac0ead63737e014e4fc86f0 Mon Sep 17 00:00:00 2001 From: Linh Tran Tuan Date: Tue, 9 Jan 2024 12:38:54 +0900 Subject: [PATCH] [Rust] [Server] New generator bases on Axum (#17549) * Rust Server - Axum based (#5) * Fix typo * Address comment * Address comment --- .github/workflows/samples-rust.yaml | 3 + .travis.yml | 1 + bin/configs/rust-axum-multipart-v3.yaml | 10 + bin/configs/rust-axum-openapi-v3.yaml | 10 + bin/configs/rust-axum-ops-v3.yaml | 8 + ...ith-fake-endpoints-models-for-testing.yaml | 10 + bin/configs/rust-axum-petstore.yaml | 9 + .../rust-axum-ping-bearer-auth-v3.yaml | 9 + bin/configs/rust-axum-test.yaml | 9 + docs/generators.md | 1 + docs/generators/rust-axum.md | 235 + .../languages/RustAxumServerCodegen.java | 1288 ++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../main/resources/rust-axum/Cargo.mustache | 68 + .../src/main/resources/rust-axum/README.md | 19 + .../main/resources/rust-axum/README.mustache | 93 + .../src/main/resources/rust-axum/gitignore | 2 + .../main/resources/rust-axum/header.mustache | 180 + .../src/main/resources/rust-axum/lib.mustache | 99 + .../main/resources/rust-axum/models.mustache | 923 +++ .../resources/rust-axum/response.mustache | 71 + .../rust-axum/server-imports.mustache | 13 + .../resources/rust-axum/server-mod.mustache | 16 + .../server-operation-validate.mustache | 221 + .../rust-axum/server-operation.mustache | 309 + .../resources/rust-axum/server-route.mustache | 15 + .../main/resources/rust-axum/types.mustache | 665 ++ samples/server/petstore/rust-axum/.gitignore | 2 + samples/server/petstore/rust-axum/Cargo.toml | 3 + .../rust-axum/output/multipart-v3/.gitignore | 2 + .../multipart-v3/.openapi-generator-ignore | 23 + .../multipart-v3/.openapi-generator/FILES | 8 + .../multipart-v3/.openapi-generator/VERSION | 1 + .../rust-axum/output/multipart-v3/Cargo.toml | 46 + .../rust-axum/output/multipart-v3/README.md | 91 + .../output/multipart-v3/src/header.rs | 197 + .../rust-axum/output/multipart-v3/src/lib.rs | 82 + .../output/multipart-v3/src/models.rs | 446 ++ .../output/multipart-v3/src/server/mod.rs | 211 + .../output/multipart-v3/src/types.rs | 665 ++ .../rust-axum/output/openapi-v3/.gitignore | 2 + .../openapi-v3/.openapi-generator-ignore | 23 + .../openapi-v3/.openapi-generator/FILES | 8 + .../openapi-v3/.openapi-generator/VERSION | 1 + .../rust-axum/output/openapi-v3/Cargo.toml | 46 + .../rust-axum/output/openapi-v3/README.md | 91 + .../rust-axum/output/openapi-v3/src/header.rs | 197 + .../rust-axum/output/openapi-v3/src/lib.rs | 482 ++ .../rust-axum/output/openapi-v3/src/models.rs | 2878 +++++++++ .../output/openapi-v3/src/server/mod.rs | 2098 ++++++ .../rust-axum/output/openapi-v3/src/types.rs | 665 ++ .../rust-axum/output/ops-v3/.gitignore | 2 + .../output/ops-v3/.openapi-generator-ignore | 23 + .../output/ops-v3/.openapi-generator/FILES | 8 + .../output/ops-v3/.openapi-generator/VERSION | 1 + .../rust-axum/output/ops-v3/Cargo.toml | 46 + .../rust-axum/output/ops-v3/README.md | 91 + .../rust-axum/output/ops-v3/src/header.rs | 180 + .../rust-axum/output/ops-v3/src/lib.rs | 586 ++ .../rust-axum/output/ops-v3/src/models.rs | 47 + .../rust-axum/output/ops-v3/src/server/mod.rs | 2581 ++++++++ .../rust-axum/output/ops-v3/src/types.rs | 665 ++ .../.gitignore | 2 + .../.openapi-generator-ignore | 23 + .../.openapi-generator/FILES | 8 + .../.openapi-generator/VERSION | 1 + .../Cargo.toml | 48 + .../README.md | 91 + .../src/header.rs | 197 + .../src/lib.rs | 679 ++ .../src/models.rs | 5624 +++++++++++++++++ .../src/server/mod.rs | 2821 +++++++++ .../src/types.rs | 665 ++ .../rust-axum/output/petstore/.gitignore | 2 + .../output/petstore/.openapi-generator-ignore | 23 + .../output/petstore/.openapi-generator/FILES | 8 + .../petstore/.openapi-generator/VERSION | 1 + .../rust-axum/output/petstore/Cargo.toml | 47 + .../rust-axum/output/petstore/README.md | 91 + .../rust-axum/output/petstore/src/header.rs | 197 + .../rust-axum/output/petstore/src/lib.rs | 441 ++ .../rust-axum/output/petstore/src/models.rs | 1215 ++++ .../output/petstore/src/server/mod.rs | 1630 +++++ .../rust-axum/output/petstore/src/types.rs | 665 ++ .../output/ping-bearer-auth/.gitignore | 2 + .../.openapi-generator-ignore | 23 + .../ping-bearer-auth/.openapi-generator/FILES | 8 + .../.openapi-generator/VERSION | 1 + .../output/ping-bearer-auth/Cargo.toml | 46 + .../output/ping-bearer-auth/README.md | 91 + .../output/ping-bearer-auth/src/header.rs | 197 + .../output/ping-bearer-auth/src/lib.rs | 51 + .../output/ping-bearer-auth/src/models.rs | 8 + .../output/ping-bearer-auth/src/server/mod.rs | 80 + .../output/ping-bearer-auth/src/types.rs | 665 ++ .../output/rust-axum-test/.gitignore | 2 + .../rust-axum-test/.openapi-generator-ignore | 23 + .../rust-axum-test/.openapi-generator/FILES | 8 + .../rust-axum-test/.openapi-generator/VERSION | 1 + .../output/rust-axum-test/Cargo.toml | 46 + .../rust-axum/output/rust-axum-test/README.md | 91 + .../output/rust-axum-test/src/header.rs | 197 + .../output/rust-axum-test/src/lib.rs | 177 + .../output/rust-axum-test/src/models.rs | 1071 ++++ .../output/rust-axum-test/src/server/mod.rs | 653 ++ .../output/rust-axum-test/src/types.rs | 665 ++ samples/server/petstore/rust-axum/pom.xml | 73 + 107 files changed, 35444 insertions(+) create mode 100644 bin/configs/rust-axum-multipart-v3.yaml create mode 100644 bin/configs/rust-axum-openapi-v3.yaml create mode 100644 bin/configs/rust-axum-ops-v3.yaml create mode 100644 bin/configs/rust-axum-petstore-with-fake-endpoints-models-for-testing.yaml create mode 100644 bin/configs/rust-axum-petstore.yaml create mode 100644 bin/configs/rust-axum-ping-bearer-auth-v3.yaml create mode 100644 bin/configs/rust-axum-test.yaml create mode 100644 docs/generators/rust-axum.md create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/README.md create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/gitignore create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/header.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/lib.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/models.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/response.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-operation-validate.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/types.mustache create mode 100644 samples/server/petstore/rust-axum/.gitignore create mode 100644 samples/server/petstore/rust-axum/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/README.md create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/README.md create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/README.md create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/ops-v3/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/README.md create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/petstore/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/petstore/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/petstore/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/petstore/README.md create mode 100644 samples/server/petstore/rust-axum/output/petstore/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/petstore/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/ping-bearer-auth/src/types.rs create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/README.md create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/rust-axum-test/src/types.rs create mode 100644 samples/server/petstore/rust-axum/pom.xml diff --git a/.github/workflows/samples-rust.yaml b/.github/workflows/samples-rust.yaml index 02d885611d6..f54dab346c6 100644 --- a/.github/workflows/samples-rust.yaml +++ b/.github/workflows/samples-rust.yaml @@ -6,11 +6,13 @@ on: - "samples/client/others/rust/**" - "samples/server/petstore/rust-server/**" - "samples/client/petstore/rust-server/**" + - "samples/server/petstore/rust-axum/**" pull_request: paths: - "samples/client/others/rust/**" - "samples/client/petstore/rust/**" - "samples/server/petstore/rust-server/**" + - "samples/server/petstore/rust-axum/**" jobs: build: @@ -24,6 +26,7 @@ jobs: - samples/client/others/rust/ - samples/client/petstore/rust/ - samples/server/petstore/rust-server/ + - samples/server/petstore/rust-axum/ steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 diff --git a/.travis.yml b/.travis.yml index 306c1da810a..c5c3dbc8459 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ cache: - $HOME/samples/client/petstore/ruby/vendor/bundle - $HOME/samples/client/petstore/python/.venv/ - $HOME/samples/server/petstore/rust-server/target + - $HOME/samples/server/petstore/rust-axum/target - $HOME/perl5 - $HOME/.cargo - $HOME/.pub-cache diff --git a/bin/configs/rust-axum-multipart-v3.yaml b/bin/configs/rust-axum-multipart-v3.yaml new file mode 100644 index 00000000000..13f7d4a3368 --- /dev/null +++ b/bin/configs/rust-axum-multipart-v3.yaml @@ -0,0 +1,10 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/multipart-v3 +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/multipart-v3.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + allowBlockingResponseSerialize: "true" + packageName: multipart-v3 +enablePostProcessFile: true diff --git a/bin/configs/rust-axum-openapi-v3.yaml b/bin/configs/rust-axum-openapi-v3.yaml new file mode 100644 index 00000000000..35ae45d6950 --- /dev/null +++ b/bin/configs/rust-axum-openapi-v3.yaml @@ -0,0 +1,10 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/openapi-v3 +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + allowBlockingValidator: "true" + packageName: openapi-v3 +enablePostProcessFile: true diff --git a/bin/configs/rust-axum-ops-v3.yaml b/bin/configs/rust-axum-ops-v3.yaml new file mode 100644 index 00000000000..c0a217166d3 --- /dev/null +++ b/bin/configs/rust-axum-ops-v3.yaml @@ -0,0 +1,8 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/ops-v3 +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ops-v3.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: ops-v3 diff --git a/bin/configs/rust-axum-petstore-with-fake-endpoints-models-for-testing.yaml b/bin/configs/rust-axum-petstore-with-fake-endpoints-models-for-testing.yaml new file mode 100644 index 00000000000..69ca39092e4 --- /dev/null +++ b/bin/configs/rust-axum-petstore-with-fake-endpoints-models-for-testing.yaml @@ -0,0 +1,10 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing +inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/petstore-with-fake-endpoints-models-for-testing.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: petstore-with-fake-endpoints-models-for-testing + publishRustRegistry: crates-io +enablePostProcessFile: true \ No newline at end of file diff --git a/bin/configs/rust-axum-petstore.yaml b/bin/configs/rust-axum-petstore.yaml new file mode 100644 index 00000000000..3149c741858 --- /dev/null +++ b/bin/configs/rust-axum-petstore.yaml @@ -0,0 +1,9 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/petstore +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: petstore +enablePostProcessFile: true diff --git a/bin/configs/rust-axum-ping-bearer-auth-v3.yaml b/bin/configs/rust-axum-ping-bearer-auth-v3.yaml new file mode 100644 index 00000000000..573a6b9f199 --- /dev/null +++ b/bin/configs/rust-axum-ping-bearer-auth-v3.yaml @@ -0,0 +1,9 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/ping-bearer-auth +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-server/ping-bearer-auth.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: ping-bearer-auth +enablePostProcessFile: true diff --git a/bin/configs/rust-axum-test.yaml b/bin/configs/rust-axum-test.yaml new file mode 100644 index 00000000000..b73b3fb5313 --- /dev/null +++ b/bin/configs/rust-axum-test.yaml @@ -0,0 +1,9 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/rust-axum-test +inputSpec: modules/openapi-generator/src/test/resources/2_0/rust-server/rust-server-test.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: rust-server-test +enablePostProcessFile: true diff --git a/docs/generators.md b/docs/generators.md index c9e07259a0e..8a72497b0c8 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -130,6 +130,7 @@ The following generators are available: * [python-flask](generators/python-flask.md) * [ruby-on-rails](generators/ruby-on-rails.md) * [ruby-sinatra](generators/ruby-sinatra.md) +* [rust-axum (beta)](generators/rust-axum.md) * [rust-server](generators/rust-server.md) * [scala-akka-http-server (beta)](generators/scala-akka-http-server.md) * [scala-finch](generators/scala-finch.md) diff --git a/docs/generators/rust-axum.md b/docs/generators/rust-axum.md new file mode 100644 index 00000000000..188ecaf27ad --- /dev/null +++ b/docs/generators/rust-axum.md @@ -0,0 +1,235 @@ +--- +title: Documentation for the rust-axum Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | rust-axum | pass this to the generate command after -g | +| generator stability | BETA | | +| generator type | SERVER | | +| generator language | Rust | | +| generator default templating engine | mustache | | +| helpTxt | Generates a Rust server library which bases on Axum. | | + +## CONFIG OPTIONS +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +|allowBlockingResponseSerialize|By default, json/form-urlencoded response serialization, which might perform a lot of compute in a future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking call to happen. It helps to improve the performance when response serialization (e.g. returns tiny data) is low cost.| |false| +|allowBlockingValidator|By default, validation process, which might perform a lot of compute in a future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking call to happen. It helps to improve the performance when validating request-data (header, path, query, body) is low cost.| |false| +|disableValidator|Disable validating request-data (header, path, query, body) against OpenAPI Schema Specification.| |false| +|packageName|Rust crate name (convention: snake_case).| |openapi| +|packageVersion|Rust crate version.| |null| + +## IMPORT MAPPING + +| Type/Alias | Imports | +| ---------- | ------- | + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | +|array|Vec| +|map|std::collections::HashMap| + + +## LANGUAGE PRIMITIVES + + + +## RESERVED WORDS + + + +## FEATURE SET + + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✗|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✗|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✗|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Uuid|✗| +|Array|✓|OAS2,OAS3 +|Null|✗|OAS3 +|AnyType|✗|OAS2,OAS3 +|Object|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✗|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✗|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✗|OAS2,OAS3 +|Examples|✗|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✗|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✗|OAS2,OAS3 +|Union|✗|OAS3 +|allOf|✗|OAS2,OAS3 +|anyOf|✗|OAS3 +|oneOf|✗|OAS3 +|not|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✓|OAS2,OAS3 +|ApiKey|✓|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✓|OAS3 +|OAuth2_Implicit|✓|OAS2,OAS3 +|OAuth2_Password|✓|OAS2,OAS3 +|OAuth2_ClientCredentials|✓|OAS2,OAS3 +|OAuth2_AuthorizationCode|✓|OAS2,OAS3 +|SignatureAuth|✗|OAS3 +|AWSV4Signature|✗|ToolingExtension + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✓|OAS2,OAS3 +|XML|✗|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✓|OAS2,OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java new file mode 100644 index 00000000000..342b1ae6652 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java @@ -0,0 +1,1288 @@ +package org.openapitools.codegen.languages; + +import com.samskivert.mustache.Mustache; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.FileSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.servers.Server; +import joptsimple.internal.Strings; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.GeneratorMetadata; +import org.openapitools.codegen.meta.Stability; +import org.openapitools.codegen.meta.features.GlobalFeature; +import org.openapitools.codegen.meta.features.ParameterFeature; +import org.openapitools.codegen.meta.features.SchemaSupportFeature; +import org.openapitools.codegen.meta.features.WireFormatFeature; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.ModelsMap; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.ModelUtils; +import org.openapitools.codegen.utils.URLPathUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; + +import static org.openapitools.codegen.utils.StringUtils.camelize; +import static org.openapitools.codegen.utils.StringUtils.underscore; + +public class RustAxumServerCodegen extends AbstractRustCodegen implements CodegenConfig { + public static final String PROJECT_NAME = "openapi-server"; + // Types + private static final String uuidType = "uuid::Uuid"; + private static final String bytesType = "ByteArray"; + private static final String octetMimeType = "application/octet-stream"; + private static final String plainTextMimeType = "text/plain"; + + private static final String xmlMimeType = "application/xml"; + private static final String textXmlMimeType = "text/xml"; + + private static final String formUrlEncodedMimeType = "application/x-www-form-urlencoded"; + + private static final String jsonMimeType = "application/json"; + // RFC 7386 support + private static final String mergePatchJsonMimeType = "application/merge-patch+json"; + // RFC 7807 Support + private static final String problemJsonMimeType = "application/problem+json"; + private static final String problemXmlMimeType = "application/problem+xml"; + + private final Logger LOGGER = LoggerFactory.getLogger(RustAxumServerCodegen.class); + // Grouping (Method, Operation) by Path. + private final Map> pathMethodOpMap = new HashMap<>(); + + protected String apiVersion = "1.0.0"; + protected String apiPath = "rust-axum"; + protected String packageName; + protected String packageVersion; + protected Boolean disableValidator = false; + protected Boolean allowBlockingValidator = false; + protected Boolean allowBlockingResponseSerialize = false; + protected String externCrateName; + protected int serverPort = 8080; + + public RustAxumServerCodegen() { + super(); + + modifyFeatureSet(features -> features + .wireFormatFeatures(EnumSet.of( + WireFormatFeature.JSON, + WireFormatFeature.Custom + )) + .excludeGlobalFeatures( + GlobalFeature.Info, + GlobalFeature.ExternalDocumentation, + GlobalFeature.Examples, + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.MultiServer, + GlobalFeature.ParameterizedServer, + GlobalFeature.ParameterStyling, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .excludeParameterFeatures( + ParameterFeature.Cookie + ) + ); + + generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) + .stability(Stability.BETA) + .build(); + + // Show the generation timestamp by default + hideGenerationTimestamp = Boolean.FALSE; + + // set the output folder here + outputFolder = "generated-code" + File.separator + "rust-axum"; + + /* + * Models. You can write model files using the modelTemplateFiles map. + * if you want to create one template for file, you can do so here. + * for multiple files for model, just put another entry in the `modelTemplateFiles` with + * a different extension + */ + modelTemplateFiles.clear(); + + /* + * Api classes. You can write classes for each Api file with the apiTemplateFiles map. + * as with models, add multiple entries with different extensions for multiple files per + * class + */ + apiTemplateFiles.clear(); + + /* + * Template Location. This is the location which templates will be read from. The generator + * will use the resource stream to attempt to read the templates. + */ + embeddedTemplateDir = templateDir = "rust-axum"; + + defaultIncludes = new HashSet<>( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet<>( + Arrays.asList( + "bool", + "char", + "i8", + "i16", + "i32", + "i64", + "u8", + "u16", + "u32", + "u64", + "isize", + "usize", + "f32", + "f64", + "str", + "String") + ); + + instantiationTypes.clear(); + instantiationTypes.put("array", "Vec"); + instantiationTypes.put("map", "std::collections::HashMap"); + + typeMapping.clear(); + typeMapping.put("number", "f64"); + typeMapping.put("integer", "i32"); + typeMapping.put("long", "i64"); + typeMapping.put("float", "f32"); + typeMapping.put("double", "f64"); + typeMapping.put("string", "String"); + typeMapping.put("UUID", uuidType); + typeMapping.put("URI", "String"); + typeMapping.put("byte", "u8"); + typeMapping.put("ByteArray", bytesType); + typeMapping.put("binary", bytesType); + typeMapping.put("boolean", "bool"); + typeMapping.put("date", "chrono::naive::NaiveDate"); + typeMapping.put("DateTime", "chrono::DateTime::"); + typeMapping.put("password", "String"); + typeMapping.put("File", "ByteArray"); + typeMapping.put("file", "ByteArray"); + typeMapping.put("array", "Vec"); + typeMapping.put("map", "std::collections::HashMap"); + typeMapping.put("object", "crate::types::Object"); + typeMapping.put("AnyType", "crate::types::Object"); + + importMapping = new HashMap<>(); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, + "Rust crate name (convention: snake_case).") + .defaultValue("openapi")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, + "Rust crate version.")); + + CliOption optDisableValidator = new CliOption("disableValidator", "Disable validating request-data (header, path, query, body) " + + "against OpenAPI Schema Specification."); + optDisableValidator.setType("bool"); + optDisableValidator.defaultValue(disableValidator.toString()); + cliOptions.add(optDisableValidator); + + CliOption optAllowBlockingValidator = new CliOption("allowBlockingValidator", "By default, validation process, which might perform a lot of compute in a " + + "future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking " + + "call to happen. It helps to improve the performance when validating request-data (header, path, query, body) is low cost."); + optAllowBlockingValidator.setType("bool"); + optAllowBlockingValidator.defaultValue(allowBlockingValidator.toString()); + cliOptions.add(optAllowBlockingValidator); + + CliOption optAllowBlockingResponseSerialize = new CliOption("allowBlockingResponseSerialize", "By default, json/form-urlencoded response serialization, which might " + + "perform a lot of compute in a future without yielding, is executed on a blocking thread via tokio::task::spawn_blocking. Set this option to true will override this behaviour and allow blocking " + + "call to happen. It helps to improve the performance when response serialization (e.g. returns tiny data) is low cost."); + optAllowBlockingResponseSerialize.setType("bool"); + optAllowBlockingResponseSerialize.defaultValue(allowBlockingResponseSerialize.toString()); + cliOptions.add(optAllowBlockingResponseSerialize); + + /* + * Additional Properties. These values can be passed to the templates and + * are available in models, apis, and supporting files + */ + additionalProperties.put("apiVersion", apiVersion); + additionalProperties.put("apiPath", apiPath); + + /* + * Supporting Files. You can write single files for the generator with the + * entire object tree available. If the input file has a suffix of `.mustache + * it will be processed by the template engine. Otherwise, it will be copied + */ + supportingFiles.add(new SupportingFile("Cargo.mustache", "", "Cargo.toml")); + supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + supportingFiles.add(new SupportingFile("lib.mustache", "src", "lib.rs")); + supportingFiles.add(new SupportingFile("models.mustache", "src", "models.rs")); + supportingFiles.add(new SupportingFile("types.mustache", "src", "types.rs")); + supportingFiles.add(new SupportingFile("header.mustache", "src", "header.rs")); + supportingFiles.add(new SupportingFile("server-mod.mustache", "src/server", "mod.rs")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md") + .doNotOverwrite()); + } + + public CodegenType getTag() { + return CodegenType.SERVER; + } + + public String getName() { + return "rust-axum"; + } + + public String getHelp() { + return "Generates a Rust server library which bases on Axum."; + } + + @Override + public Mustache.Compiler processCompiler(Mustache.Compiler compiler) { + return super.processCompiler(compiler).emptyStringIsFalse(true).zeroIsFalse(true); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (StringUtils.isEmpty(System.getenv("RUST_POST_PROCESS_FILE"))) { + LOGGER.info("Environment variable RUST_POST_PROCESS_FILE not defined. rustfmt will be used" + + " by default. To choose a different tool, try" + + " 'export RUST_POST_PROCESS_FILE=\"/usr/local/bin/rustfmt\"' (Linux/Mac)"); + LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` " + + " (--enable-post-process-file for CLI)."); + } + + if (!Boolean.TRUE.equals(ModelUtils.isGenerateAliasAsModel())) { + LOGGER.warn("generateAliasAsModel is set to false, which means array/map will be generated as model instead and the resulting code may have issues. Please enable `generateAliasAsModel` to address the issue."); + } + + setPackageName((String) additionalProperties.getOrDefault(CodegenConstants.PACKAGE_NAME, "openapi")); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) { + setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION)); + } + + additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + additionalProperties.put("externCrateName", externCrateName); + + if (additionalProperties.containsKey("disableValidator")) { + disableValidator = convertPropertyToBooleanAndWriteBack("disableValidator"); + } else { + additionalProperties.put("disableValidator", disableValidator); + } + + if (additionalProperties.containsKey("allowBlockingValidator")) { + allowBlockingValidator = convertPropertyToBooleanAndWriteBack("allowBlockingValidator"); + } else { + additionalProperties.put("allowBlockingValidator", allowBlockingValidator); + } + + if (additionalProperties.containsKey("allowBlockingResponseSerialize")) { + allowBlockingValidator = convertPropertyToBooleanAndWriteBack("allowBlockingResponseSerialize"); + } else { + additionalProperties.put("allowBlockingResponseSerialize", allowBlockingResponseSerialize); + } + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + + // Also set the extern crate name, which has any '-' replace with a '_'. + this.externCrateName = packageName.replace('-', '_'); + } + + public void setPackageVersion(String packageVersion) { + this.packageVersion = packageVersion; + } + + @Override + public String apiPackage() { + return apiPath; + } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + Info info = openAPI.getInfo(); + + URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides()); + additionalProperties.put("serverHost", url.getHost()); + additionalProperties.put("serverPort", URLPathUtils.getPort(url, serverPort)); + + if (packageVersion == null || packageVersion.isEmpty()) { + List versionComponents = new ArrayList<>(Arrays.asList(info.getVersion().split("[.]"))); + if (versionComponents.isEmpty()) { + versionComponents.add("1"); + } + while (versionComponents.size() < 3) { + versionComponents.add("0"); + } + + setPackageVersion(StringUtils.join(versionComponents, ".")); + } + + additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion); + } + + @Override + public String toApiName(String name) { + if (name.isEmpty()) { + return "default"; + } + return sanitizeIdentifier(name, CasingType.SNAKE_CASE, "api", "API", true); + } + + /** + * Location to write api files. You can use the apiPackage() as defined when the class is + * instantiated + */ + @Override + public String apiFileFolder() { + return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String toOperationId(String operationId) { + // rust-axum uses camel case instead + return sanitizeIdentifier(operationId, CasingType.CAMEL_CASE, "call", "method", true); + } + + @Override + public String toEnumValue(String value, String datatype) { + // rust-axum templates expect value to be in quotes + return "\"" + super.toEnumValue(value, datatype) + "\""; + } + + private boolean isMimetypeXml(String mimetype) { + return mimetype.toLowerCase(Locale.ROOT).startsWith(xmlMimeType) || + mimetype.toLowerCase(Locale.ROOT).startsWith(problemXmlMimeType) || + mimetype.toLowerCase(Locale.ROOT).startsWith(textXmlMimeType); + } + + private boolean isMimetypeJson(String mimetype) { + return mimetype.toLowerCase(Locale.ROOT).startsWith(jsonMimeType) || + mimetype.toLowerCase(Locale.ROOT).startsWith(mergePatchJsonMimeType) || + mimetype.toLowerCase(Locale.ROOT).startsWith(problemJsonMimeType); + } + + private boolean isMimetypeWwwFormUrlEncoded(String mimetype) { + return mimetype.toLowerCase(Locale.ROOT).startsWith(formUrlEncodedMimeType); + } + + private boolean isMimetypeMultipartFormData(String mimetype) { + return mimetype.toLowerCase(Locale.ROOT).startsWith("multipart/form-data"); + } + + private boolean isMimetypeMultipartRelated(String mimetype) { + return mimetype.toLowerCase(Locale.ROOT).startsWith("multipart/related"); + } + + private boolean isMimetypeUnknown(String mimetype) { + return "*/*".equals(mimetype); + } + + boolean isMimetypePlain(String mimetype) { + return !(isMimetypeUnknown(mimetype) || + isMimetypeJson(mimetype) || + isMimetypeWwwFormUrlEncoded(mimetype) || + isMimetypeMultipartFormData(mimetype) || + isMimetypeMultipartRelated(mimetype)); + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List servers) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers); + + String pathFormatString = op.path; + for (CodegenParameter param : op.pathParams) { + // Replace {baseName} with {paramName} for format string + String paramSearch = "{" + param.baseName + "}"; + String paramReplace = "{" + param.paramName + "}"; + + pathFormatString = pathFormatString.replace(paramSearch, paramReplace); + } + op.vendorExtensions.put("x-path-format-string", pathFormatString); + + boolean hasPathParams = !op.pathParams.isEmpty(); + + // String for formatting the path for a client to make a request + String formatPath = op.path; + + for (CodegenParameter param : op.pathParams) { + // Replace {baseName} with {paramName} for format string + String paramSearch = "{" + param.baseName + "}"; + String paramReplace = "{" + param.paramName + "}"; + + formatPath = formatPath.replace(paramSearch, paramReplace); + } + + String underscoredOperationId = underscore(op.operationId); + op.vendorExtensions.put("x-operation-id", underscoredOperationId); + op.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId.toUpperCase(Locale.ROOT)); + String vendorExtensionPath = op.path.replace("{", ":").replace("}", ""); + op.vendorExtensions.put("x-path", vendorExtensionPath); + op.vendorExtensions.put("x-has-path-params", hasPathParams); + op.vendorExtensions.put("x-path-format-string", formatPath); + + if (!op.isCallbackRequest) { + // group route by path + String axumPath = op.path; + for (CodegenParameter param : op.pathParams) { + // Replace {baseName} with {paramName} for format string + String paramSearch = "{" + param.baseName + "}"; + String paramReplace = ":" + param.paramName; + + axumPath = axumPath.replace(paramSearch, paramReplace); + } + pathMethodOpMap + .computeIfAbsent(axumPath, (key) -> new ArrayList<>()) + .add(new MethodOperation(op.httpMethod.toLowerCase(Locale.ROOT), underscoredOperationId)); + } + + String vendorExtensionHttpMethod = op.httpMethod.toUpperCase(Locale.ROOT); + op.vendorExtensions.put("x-http-method", vendorExtensionHttpMethod); + + if (!op.vendorExtensions.containsKey("x-must-use-response")) { + // If there's more than one response, than by default the user must explicitly handle them + op.vendorExtensions.put("x-must-use-response", op.responses.size() > 1); + } + + for (CodegenParameter param : op.allParams) { + processParam(param); + } + + // Determine the types that this operation produces. `getProducesInfo` + // simply lists all the types, and then we add the correct imports to + // the generated library. + Set producesInfo = getProducesInfo(openAPI, operation); + boolean producesPlainText = false; + boolean producesFormUrlEncoded = false; + if (producesInfo != null && !producesInfo.isEmpty()) { + List produces = new ArrayList<>(producesInfo); + + List> c = new ArrayList<>(); + for (String mimeType : produces) { + Map mediaType = new HashMap<>(); + + if (isMimetypeWwwFormUrlEncoded(mimeType)) { + producesFormUrlEncoded = true; + } else if (isMimetypePlain(mimeType)) { + producesPlainText = true; + } + + mediaType.put("mediaType", mimeType); + c.add(mediaType); + } + + op.produces = c; + op.hasProduces = true; + } + + for (CodegenParameter param : op.headerParams) { + processParam(param); + + // Give header params a name in camel case. CodegenParameters don't have a nameInCamelCase property. + param.vendorExtensions.put("x-type-name", toModelName(param.baseName)); + } + + // Set for deduplication of response IDs + final Set responseIds = new HashSet<>(); + + for (CodegenResponse rsp : op.responses) { + // Get the original API response, so we get process the schema + // directly. + ApiResponse original; + if ("0".equals(rsp.code)) { + original = operation.getResponses().get("default"); + } else { + original = operation.getResponses().get(rsp.code); + } + String[] words = rsp.message.split("[^A-Za-z ]"); + + // Create a unique responseID for this response. + String responseId; + + if (rsp.vendorExtensions.containsKey("x-response-id")) { + // If it's been specified directly, use that. + responseId = (String) rsp.vendorExtensions.get("x-response-id"); + } else if ((words.length != 0) && (!words[0].trim().isEmpty())) { + // If there's a description, build it from the description. + responseId = camelize(words[0].replace(" ", "_")); + } else { + // Otherwise fall back to the http response code. + responseId = "Status" + rsp.code; + } + + // Deduplicate response IDs that would otherwise contain the same + // text. We rely on the ID being unique, but since we form it from + // the raw description field we can't require that the spec writer + // provides unique descriptions. + int idTieBreaker = 2; + while (responseIds.contains(responseId)) { + String trial = String.format(Locale.ROOT, "%s_%d", responseId, idTieBreaker); + if (!responseIds.contains(trial)) { + responseId = trial; + } else { + idTieBreaker++; + } + } + + responseIds.add(responseId); + + String underscoredResponseId = underscore(responseId).toUpperCase(Locale.ROOT); + rsp.vendorExtensions.put("x-response-id", responseId); + rsp.vendorExtensions.put("x-uppercase-response-id", underscoredResponseId.toUpperCase(Locale.ROOT)); + rsp.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId.toUpperCase(Locale.ROOT)); + if (rsp.dataType != null) { + String uppercaseDataType = (rsp.dataType.replace("models::", "")).toUpperCase(Locale.ROOT); + rsp.vendorExtensions.put("x-uppercase-data-type", uppercaseDataType); + + // Get the mimetype which is produced by this response. Note + // that although in general responses produces a set of + // different mimetypes currently we only support 1 per + // response. + String firstProduces = null; + + if (original.getContent() != null) { + firstProduces = original.getContent().keySet().stream().findFirst().orElse(null); + } + + // The output mime type. This allows us to do sensible fallback + // to JSON rather than using only the default operation + // mimetype. + String outputMime; + + if (firstProduces == null) { + if (producesFormUrlEncoded) { + outputMime = formUrlEncodedMimeType; + } else if (producesPlainText) { + if (bytesType.equals(rsp.dataType)) { + outputMime = octetMimeType; + } else { + outputMime = plainTextMimeType; + } + } else { + outputMime = jsonMimeType; + } + } else { + // If we know exactly what mimetype this response is + // going to produce, then use that. If we have not found + // anything, then we'll fall back to the 'producesXXX' + // definitions we worked out above for the operation as a + // whole. + if (isMimetypeWwwFormUrlEncoded(firstProduces)) { + producesFormUrlEncoded = true; + producesPlainText = false; + } else if (isMimetypePlain(firstProduces)) { + producesFormUrlEncoded = false; + producesPlainText = true; + } else { + producesFormUrlEncoded = false; + producesPlainText = false; + } + + outputMime = firstProduces; + } + + // As we don't support XML, fallback to plain text + if (isMimetypeXml(outputMime)) { + outputMime = plainTextMimeType; + } + + rsp.vendorExtensions.put("x-mime-type", outputMime); + + // Write out the type of data we actually expect this response + // to make. + if (producesFormUrlEncoded) { + rsp.vendorExtensions.put("x-produces-form-urlencoded", true); + } else if (producesPlainText) { + // Plain text means that there is not structured data in + // this response. So it'll either be a UTF-8 encoded string + // 'plainText' or some generic 'bytes'. + // + // Note that we don't yet distinguish between string/binary + // and string/bytes - that is we don't auto-detect whether + // base64 encoding should be done. They both look like + // 'producesBytes'. + if (bytesType.equals(rsp.dataType)) { + rsp.vendorExtensions.put("x-produces-bytes", true); + } else { + rsp.vendorExtensions.put("x-produces-plain-text", true); + } + } else { + rsp.vendorExtensions.put("x-produces-json", true); + // If the data type is just "object", then ensure that the + // Rust data type is "crate::types::Object". This allows us + // to define APIs that can return arbitrary JSON bodies. + if ("object".equals(rsp.dataType)) { + rsp.dataType = "crate::types::Object"; + } + } + } + for (CodegenProperty header : rsp.headers) { + if (uuidType.equals(header.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + header.nameInCamelCase = toModelName(header.baseName); + header.nameInLowerCase = header.baseName.toLowerCase(Locale.ROOT); + } + } + + for (CodegenParameter header : op.headerParams) { + header.nameInLowerCase = header.baseName.toLowerCase(Locale.ROOT); + } + + for (CodegenProperty header : op.responseHeaders) { + if (uuidType.equals(header.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + header.nameInCamelCase = toModelName(header.baseName); + header.nameInLowerCase = header.baseName.toLowerCase(Locale.ROOT); + } + + return op; + } + + private void processParam(CodegenParameter param) { + // If a parameter uses UUIDs, we need to import the UUID package. + if (uuidType.equals(param.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + + if (Boolean.TRUE.equals(param.isFreeFormObject)) { + param.vendorExtensions.put("x-format-string", "{:?}"); + } else if (param.isString) { + param.vendorExtensions.put("x-format-string", "\\\"{}\\\""); + } else if (param.isPrimitiveType) { + if ((param.isByteArray) || (param.isBinary)) { + // Binary primitive types don't implement `Display`. + param.vendorExtensions.put("x-format-string", "{:?}"); + } else { + param.vendorExtensions.put("x-format-string", "{}"); + } + } else if (param.isArray) { + param.vendorExtensions.put("x-format-string", "{:?}"); + } else { + param.vendorExtensions.put("x-format-string", "{:?}"); + } + + if (!param.required) { + if ((("date-time".equals(param.dataFormat)) || ("date".equals(param.dataFormat)))) { + param.vendorExtensions.put("x-format-string", "{:?}"); + } else { + // Not required, so override the format string and example + param.vendorExtensions.put("x-format-string", "{:?}"); + } + } + } + + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + OperationMap operations = objs.getOperations(); + List operationList = operations.getOperation(); + + for (CodegenOperation op : operationList) { + postProcessOperationWithModels(op); + } + + return objs; + } + + private void postProcessOperationWithModels(CodegenOperation op) { + boolean consumesJson = false; + boolean consumesPlainText = false; + boolean consumesFormUrlEncoded = false; + + if (op.consumes != null) { + for (Map consume : op.consumes) { + final String mediaType = consume.get("mediaType"); + if (mediaType != null) { + if (isMimetypeJson(mediaType)) { + consumesJson = true; + } else if (isMimetypeWwwFormUrlEncoded(mediaType)) { + consumesFormUrlEncoded = true; + } else if (isMimetypePlain(mediaType)) { + consumesPlainText = true; + } else if (isMimetypeMultipartFormData(mediaType)) { + op.vendorExtensions.put("x-consumes-multipart", true); + additionalProperties.put("apiUsesMultipartFormData", true); + additionalProperties.put("apiUsesMultipart", true); + } else if (isMimetypeMultipartRelated(mediaType)) { + op.vendorExtensions.put("x-consumes-multipart-related", true); + additionalProperties.put("apiUsesMultipartRelated", true); + additionalProperties.put("apiUsesMultipart", true); + } + } + } + } + + String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT); + if (op.bodyParam != null) { + // Default to consuming json + op.bodyParam.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId); + if (consumesJson) { + op.bodyParam.vendorExtensions.put("x-consumes-json", true); + } else if (consumesFormUrlEncoded) { + op.bodyParam.vendorExtensions.put("x-consumes-form-urlencoded", true); + } else if (consumesPlainText) { + op.bodyParam.vendorExtensions.put("x-consumes-plain-text", true); + } else { + op.bodyParam.vendorExtensions.put("x-consumes-json", true); + } + } + + for (CodegenParameter param : op.bodyParams) { + processParam(param); + + param.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId); + + // Default to producing json if nothing else is specified + if (consumesJson) { + param.vendorExtensions.put("x-consumes-json", true); + } else if (consumesFormUrlEncoded) { + param.vendorExtensions.put("x-consumes-form-urlencoded", true); + } else if (consumesPlainText) { + param.vendorExtensions.put("x-consumes-plain-text", true); + } else { + param.vendorExtensions.put("x-consumes-json", true); + } + } + + for (CodegenParameter param : op.queryParams) { + // If the MIME type is JSON, mark it. We don't currently support any other MIME types. + if (param.contentType != null && isMimetypeJson(param.contentType)) { + param.vendorExtensions.put("x-consumes-json", true); + } + } + + for (CodegenParameter param : op.formParams) { + processParam(param); + } + + for (CodegenParameter header : op.headerParams) { + header.nameInLowerCase = header.baseName.toLowerCase(Locale.ROOT); + } + + for (CodegenProperty header : op.responseHeaders) { + if (uuidType.equals(header.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + header.nameInCamelCase = toModelName(header.baseName); + header.nameInLowerCase = header.baseName.toLowerCase(Locale.ROOT); + } + + if (op.authMethods != null) { + boolean headerAuthMethods = false; + + for (CodegenSecurity s : op.authMethods) { + if (s.isApiKey && s.isKeyInHeader) { + s.vendorExtensions.put("x-api-key-name", toModelName(s.keyParamName)); + headerAuthMethods = true; + } + + if (s.isBasicBasic || s.isBasicBearer || s.isOAuth) { + headerAuthMethods = true; + } + } + + if (headerAuthMethods) { + op.vendorExtensions.put("x-has-header-auth-methods", "true"); + } + } + } + + @Override + public boolean isDataTypeFile(final String dataType) { + return dataType != null && dataType.equals(typeMapping.get("File")); + } + + /** + * Add operation to group + * + * @param tag name of the tag + * @param resourcePath path of the resource + * @param operation OAS Operation object + * @param co Codegen Operation object + * @param operations map of Codegen operations + */ + @SuppressWarnings("static-method") + @Override + public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation + co, Map> operations) { + // only generate operation for the first tag of the tags + if (tag != null && co.tags.size() > 1) { + String expectedTag = sanitizeTag(co.tags.get(0).getName()); + if (!tag.equals(expectedTag)) { + LOGGER.info("generated skip additional tag `{}` with operationId={}", tag, co.operationId); + return; + } + } + super.addOperationToGroup(tag, resourcePath, operation, co, operations); + } + + // This is a really terrible hack. We're working around the fact that the + // base version of `fromRequestBody` checks to see whether the body is a + // ref. If so, it unwraps the reference and replaces it with its inner + // type. This causes problems in rust-axum, as it means that we use inner + // types in the API, rather than the correct outer type. + // + // Thus, we grab the inner schema beforehand, and then tinker afterward to + // restore things to sensible values. + @Override + public CodegenParameter fromRequestBody(RequestBody body, Set imports, String bodyParameterName) { + Schema original_schema = ModelUtils.getSchemaFromRequestBody(body); + CodegenParameter codegenParameter = super.fromRequestBody(body, imports, bodyParameterName); + + if (StringUtils.isNotBlank(original_schema.get$ref())) { + // Undo the mess `super.fromRequestBody` made - re-wrap the inner + // type. + codegenParameter.dataType = getTypeDeclaration(original_schema); + codegenParameter.isPrimitiveType = false; + codegenParameter.isArray = false; + codegenParameter.isString = false; + codegenParameter.isByteArray = ModelUtils.isByteArraySchema(original_schema); + + + // This is a model, so should only have an example if explicitly + // defined. + if (codegenParameter.vendorExtensions != null && codegenParameter.vendorExtensions.containsKey("x-example")) { + codegenParameter.example = Json.pretty(codegenParameter.vendorExtensions.get("x-example")); + } else if (!codegenParameter.required) { + //mandatory parameter use the example in the yaml. if no example, it is also null. + codegenParameter.example = null; + } + } + + return codegenParameter; + } + + @Override + public String getTypeDeclaration(Schema p) { + if (ModelUtils.isArraySchema(p)) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + String innerType = getTypeDeclaration(inner); + return typeMapping.get("array") + "<" + innerType + ">"; + } else if (ModelUtils.isMapSchema(p)) { + Schema inner = ModelUtils.getAdditionalProperties(p); + String innerType = getTypeDeclaration(inner); + StringBuilder typeDeclaration = new StringBuilder(typeMapping.get("map")).append("<").append(typeMapping.get("string")).append(", "); + typeDeclaration.append(innerType).append(">"); + return typeDeclaration.toString(); + } else if (!StringUtils.isEmpty(p.get$ref())) { + String datatype; + try { + datatype = p.get$ref(); + + if (datatype.indexOf("#/components/schemas/") == 0) { + datatype = toModelName(datatype.substring("#/components/schemas/".length())); + datatype = "models::" + datatype; + } + } catch (Exception e) { + LOGGER.warn("Error obtaining the datatype from schema (model):{}. Datatype default to Object", p); + datatype = "Object"; + LOGGER.error(e.getMessage(), e); + } + return datatype; + } else if (p instanceof FileSchema) { + return typeMapping.get("File"); + } + + return super.getTypeDeclaration(p); + } + + @Override + public String toInstantiationType(Schema p) { + if (ModelUtils.isArraySchema(p)) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return instantiationTypes.get("array") + "<" + getSchemaType(inner) + ">"; + } else if (ModelUtils.isMapSchema(p)) { + Schema inner = ModelUtils.getAdditionalProperties(p); + return instantiationTypes.get("map") + "<" + typeMapping.get("string") + ", " + getSchemaType(inner) + ">"; + } else { + return null; + } + } + + @Override + public CodegenModel fromModel(String name, Schema model) { + LOGGER.trace("Creating model from schema: {}", model); + + Map allDefinitions = ModelUtils.getSchemas(this.openAPI); + CodegenModel mdl = super.fromModel(name, model); + + mdl.vendorExtensions.put("x-upper-case-name", name.toUpperCase(Locale.ROOT)); + if (!StringUtils.isEmpty(model.get$ref())) { + Schema schema = allDefinitions.get(ModelUtils.getSimpleRef(model.get$ref())); + mdl.dataType = typeMapping.get(schema.getType()); + } + if (ModelUtils.isArraySchema(model)) { + if (typeMapping.containsKey(mdl.arrayModelType)) { + mdl.arrayModelType = typeMapping.get(mdl.arrayModelType); + } else { + mdl.arrayModelType = toModelName(mdl.arrayModelType); + } + } else if ((!mdl.anyOf.isEmpty()) || (!mdl.oneOf.isEmpty())) { + mdl.dataType = getSchemaType(model); + } + + Schema additionalProperties = ModelUtils.getAdditionalProperties(model); + + if (additionalProperties != null) { + mdl.additionalPropertiesType = getTypeDeclaration(additionalProperties); + } + + LOGGER.trace("Created model: {}", mdl); + + return mdl; + } + + @Override + public Map postProcessAllModels(Map objs) { + Map newObjs = super.postProcessAllModels(objs); + + //Index all CodegenModels by model name. + HashMap allModels = new HashMap<>(); + for (Map.Entry entry : objs.entrySet()) { + String modelName = toModelName(entry.getKey()); + List models = entry.getValue().getModels(); + for (ModelMap mo : models) { + allModels.put(modelName, mo.getModel()); + } + } + + for (Map.Entry entry : allModels.entrySet()) { + CodegenModel model = entry.getValue(); + + if (uuidType.equals(model.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + + for (CodegenProperty prop : model.vars) { + if (uuidType.equals(prop.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + + if (uuidType.equals(prop.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + } + } + + return newObjs; + } + + @Override + public Map postProcessSupportingFileData(Map bundle) { + generateYAMLSpecFile(bundle); + + final List pathMethodOps = pathMethodOpMap.entrySet().stream() + .map(entry -> { + ArrayList methodOps = entry.getValue(); + methodOps.sort(Comparator.comparing(a -> a.method)); + return new PathMethodOperations(entry.getKey(), methodOps); + }) + .sorted(Comparator.comparing(a -> a.path)) + .collect(Collectors.toList()); + bundle.put("pathMethodOps", pathMethodOps); + + return super.postProcessSupportingFileData(bundle); + } + + @Override + public String toDefaultValue(Schema p) { + String defaultValue = null; + if ((ModelUtils.isNullable(p)) && (p.getDefault() != null) && ("null".equalsIgnoreCase(p.getDefault().toString()))) + return "Nullable::Null"; + else if (ModelUtils.isBooleanSchema(p)) { + if (p.getDefault() != null) { + if ("false".equalsIgnoreCase(p.getDefault().toString())) + defaultValue = "false"; + else + defaultValue = "true"; + } + } else if (ModelUtils.isNumberSchema(p)) { + if (p.getDefault() != null) { + defaultValue = p.getDefault().toString(); + } + } else if (ModelUtils.isIntegerSchema(p)) { + if (p.getDefault() != null) { + defaultValue = p.getDefault().toString(); + } + } else if (ModelUtils.isStringSchema(p)) { + if (p.getDefault() != null) { + defaultValue = "\"" + p.getDefault() + "\".to_string()"; + } + } + if ((defaultValue != null) && (ModelUtils.isNullable(p))) + defaultValue = "Nullable::Present(" + defaultValue + ")"; + return defaultValue; + } + + @Override + public String toOneOfName(List names, Schema composedSchema) { + List schemas = ModelUtils.getInterfaces(composedSchema); + + List types = new ArrayList<>(); + for (Schema s : schemas) { + types.add(getTypeDeclaration(s)); + } + return "swagger::OneOf" + types.size() + "<" + String.join(",", types) + ">"; + } + + @Override + public String toAnyOfName(List names, Schema composedSchema) { + List schemas = ModelUtils.getInterfaces(composedSchema); + + List types = new ArrayList<>(); + for (Schema s : schemas) { + types.add(getTypeDeclaration(s)); + } + return "swagger::AnyOf" + types.size() + "<" + String.join(",", types) + ">"; + } + + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + if (!languageSpecificPrimitives.contains(property.dataType)) { + // If we use a more qualified model name, then only camelize the actual type, not the qualifier. + if (property.dataType.contains(":")) { + int position = property.dataType.lastIndexOf(":"); + property.dataType = property.dataType.substring(0, position) + camelize(property.dataType.substring(position)); + } else { + property.dataType = camelize(property.dataType); + } + property.isPrimitiveType = property.isContainer && languageSpecificPrimitives.contains(typeMapping.get(property.complexType)); + } else { + property.isPrimitiveType = true; + } + + // Integer type fitting + if (Objects.equals(property.baseType, "integer")) { + + BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null); + BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null); + + boolean unsigned = canFitIntoUnsigned(minimum, property.getExclusiveMinimum()); + + if (Strings.isNullOrEmpty(property.dataFormat)) { + property.dataType = bestFittingIntegerType(minimum, + property.getExclusiveMinimum(), + maximum, + property.getExclusiveMaximum(), + true); + } else { + switch (property.dataFormat) { + // custom integer formats (legacy) + case "uint32": + property.dataType = "u32"; + break; + case "uint64": + property.dataType = "u64"; + break; + case "int32": + property.dataType = unsigned ? "u32" : "i32"; + break; + case "int64": + property.dataType = unsigned ? "u64" : "i64"; + break; + default: + LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", property.dataFormat); + property.dataType = bestFittingIntegerType(minimum, + property.getExclusiveMinimum(), + maximum, + property.getExclusiveMaximum(), + true); + } + } + } + + property.name = underscore(property.name); + + if (!property.required) { + property.defaultValue = (property.defaultValue != null) ? "Some(" + property.defaultValue + ")" : "None"; + } + + // If a property has no type defined in the schema, it can take values of any type. + // This clashes with Rust being statically typed. Hence, assume it's sent as a json + // blob and return the json value to the user of the API and let the user determine + // the type from the value. If the property has no type, at this point it will have + // baseType "object" allowing us to identify such properties. Moreover, set to not + // nullable, we can use the crate::types::Object::Null enum variant. + if ("object".equals(property.baseType)) { + property.dataType = "crate::types::Object"; + property.isNullable = false; + } + } + + @Override + public ModelsMap postProcessModels(ModelsMap objs) { + for (ModelMap mo : objs.getModels()) { + CodegenModel cm = mo.getModel(); + + LOGGER.trace("Post processing model: {}", cm); + + if ("object".equals(cm.dataType)) { + // Object isn't a sensible default. Instead, we set it to + // 'null'. This ensures that we treat this model as a struct + // with multiple parameters. + cm.dataType = null; + } else if ("map".equals(cm.dataType)) { + if (!cm.allVars.isEmpty() || cm.additionalPropertiesType == null) { + // We don't yet support `additionalProperties` that also have + // properties. If we see variables, we ignore the + // `additionalProperties` type ('map') and warn the user. This + // will produce code that compiles, but won't feature the + // `additionalProperties` - but that's likely more useful to + // the user than the alternative. + LOGGER.warn("Ignoring additionalProperties (see https://github.com/OpenAPITools/openapi-generator/issues/318) alongside defined properties"); + cm.dataType = null; + } else { + cm.dataType = "std::collections::HashMap"; + } + } else if (cm.dataType != null) { + // We need to hack about with single-parameter models to + // get them recognised correctly. + cm.isAlias = false; + cm.dataType = typeMapping.get(cm.dataType); + + if (uuidType.equals(cm.dataType)) { + additionalProperties.put("apiUsesUuid", true); + } + } + + cm.vendorExtensions.put("x-is-string", "String".equals(cm.dataType)); + } + return super.postProcessModelsEnum(objs); + } + + @Override + public void postProcessFile(File file, String fileType) { + if (file == null) { + return; + } + + String commandPrefix = System.getenv("RUST_POST_PROCESS_FILE"); + if (StringUtils.isEmpty(commandPrefix)) { + commandPrefix = "rustfmt"; + } + + // only process files with .rs extension + if ("rs".equals(FilenameUtils.getExtension(file.toString()))) { + try { + Process p = Runtime.getRuntime().exec(new String[]{commandPrefix, "--edition", "2021", file.toString()}); + int exitValue = p.waitFor(); + if (exitValue != 0) { + LOGGER.error("Error running the command ({} {}). Exit code: {}", commandPrefix, file, exitValue); + } else { + LOGGER.info("Successfully executed: {} {}", commandPrefix, file); + } + } catch (InterruptedException | IOException e) { + LOGGER.error("Error running the command ({} {}). Exception: {}", commandPrefix, file, e.getMessage()); + // Restore interrupted state + Thread.currentThread().interrupt(); + } + } + } + + @Override + protected void updateParameterForString(CodegenParameter codegenParameter, Schema parameterSchema) { + /* + we have a custom version of this function to set isString to false for uuid + */ + if (ModelUtils.isEmailSchema(parameterSchema)) { + codegenParameter.isEmail = true; + } else if (ModelUtils.isUUIDSchema(parameterSchema)) { + codegenParameter.setIsString(false); + codegenParameter.isUuid = true; + } else if (ModelUtils.isByteArraySchema(parameterSchema)) { + codegenParameter.setIsString(false); + codegenParameter.isByteArray = true; + codegenParameter.isPrimitiveType = true; + } else if (ModelUtils.isBinarySchema(parameterSchema)) { + codegenParameter.isBinary = true; + codegenParameter.isFile = true; // file = binary in OAS3 + codegenParameter.isPrimitiveType = true; + } else if (ModelUtils.isDateSchema(parameterSchema)) { + codegenParameter.setIsString(false); // for backward compatibility with 2.x + codegenParameter.isDate = true; + codegenParameter.isPrimitiveType = true; + } else if (ModelUtils.isDateTimeSchema(parameterSchema)) { + codegenParameter.setIsString(false); // for backward compatibility with 2.x + codegenParameter.isDateTime = true; + codegenParameter.isPrimitiveType = true; + } else if (ModelUtils.isDecimalSchema(parameterSchema)) { // type: string, format: number + codegenParameter.setIsString(false); + codegenParameter.isDecimal = true; + codegenParameter.isPrimitiveType = true; + } + if (Boolean.TRUE.equals(codegenParameter.isString)) { + codegenParameter.isPrimitiveType = true; + } + } + + @Override + protected void updatePropertyForAnyType(CodegenProperty property, Schema p) { + /* + * we have a custom version of this function to not set isNullable to true + */ + // The 'null' value is allowed when the OAS schema is 'any type'. + // See https://github.com/OAI/OpenAPI-Specification/issues/1389 + if (Boolean.FALSE.equals(p.getNullable())) { + LOGGER.warn("Schema '{}' is any type, which includes the 'null' value. 'nullable' cannot be set to 'false'", p.getName()); + } + if (languageSpecificPrimitives.contains(property.dataType)) { + property.isPrimitiveType = true; + } + if (ModelUtils.isMapSchema(p)) { + // an object or anyType composed schema that has additionalProperties set + // some of our code assumes that any type schema with properties defined will be a map + // even though it should allow in any type and have map constraints for properties + updatePropertyForMap(property, p); + } + } + + @Override + protected String getParameterDataType(Parameter parameter, Schema schema) { + if (parameter.get$ref() != null) { + String refName = ModelUtils.getSimpleRef(parameter.get$ref()); + return toModelName(refName); + } + return null; + } + + static class PathMethodOperations { + public String path; + public ArrayList methodOperations; + + PathMethodOperations(String path, ArrayList methodOperations) { + this.path = path; + this.methodOperations = methodOperations; + } + } + + static class MethodOperation { + public String method; + public String operationID; + + MethodOperation(String method, String operationID) { + this.method = method; + this.operationID = operationID; + } + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index ffb879c5794..e605d387806 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -143,3 +143,4 @@ org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.ZapierClientCodegen +org.openapitools.codegen.languages.RustAxumServerCodegen diff --git a/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache b/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache new file mode 100644 index 00000000000..d313de7116b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache @@ -0,0 +1,68 @@ +[package] +name = "{{{packageName}}}" +version = "{{{packageVersion}}}" +{{#infoEmail}} +authors = ["{{{.}}}"] +{{/infoEmail}} +{{^infoEmail}} +authors = ["OpenAPI Generator team and contributors"] +{{/infoEmail}} +{{#appDescription}} +description = "{{{.}}}" +{{/appDescription}} +{{#licenseInfo}} +license = "{{.}}" +{{/licenseInfo}} +edition = "2021" +{{#publishRustRegistry}} +publish = ["{{.}}"] +{{/publishRustRegistry}} +{{#repositoryUrl}} +repository = "{{.}}" +{{/repositoryUrl}} +{{#documentationUrl}} +documentation = "{{.}}" +{{/documentationUrl}} +{{#homePageUrl}} +homepage = "{{.}} +{{/homePageUrl}} + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/modules/openapi-generator/src/main/resources/rust-axum/README.md b/modules/openapi-generator/src/main/resources/rust-axum/README.md new file mode 100644 index 00000000000..f67e436393b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/README.md @@ -0,0 +1,19 @@ +# Rust Axum Server Generator Templates + +The Rust Axum Server Generator templates use Mustache Partials. + +The following tree shows which templates include which: + +- `Cargo.mustache` +- `gitignore` +- `header.mustache` +- `lib.mustache` + - `response.mustache` +- `models.mustache` +- `README.mustache` +- `server-mod.mustache` + - `server-imports.mustache` + - `server-route.mustache` + - `server-operation-validate.mustache` + - `server-operation.mustache` +- `types.mustache` diff --git a/modules/openapi-generator/src/main/resources/rust-axum/README.mustache b/modules/openapi-generator/src/main/resources/rust-axum/README.mustache new file mode 100644 index 00000000000..3fe0dcd0dbe --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/README.mustache @@ -0,0 +1,93 @@ +# Rust API for {{{packageName}}} + +{{#appDescriptionWithNewLines}} +{{{.}}} +{{/appDescriptionWithNewLines}} + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: {{{appVersion}}} +{{^hideGenerationTimestamp}}- Build date: {{{generatedDate}}}{{/hideGenerationTimestamp}} + +{{#infoUrl}}For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}){{/infoUrl}} + +This autogenerated project defines an API crate `{{{packageName}}}` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl {{{packageName}}}::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = {{{packageName}}}::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/modules/openapi-generator/src/main/resources/rust-axum/gitignore b/modules/openapi-generator/src/main/resources/rust-axum/gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/modules/openapi-generator/src/main/resources/rust-axum/header.mustache b/modules/openapi-generator/src/main/resources/rust-axum/header.mustache new file mode 100644 index 00000000000..4d1cc4c6dcc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/header.mustache @@ -0,0 +1,180 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse {} as a string: {}", + stringify!($t), e)), + }, + Err(e) => Err(format!("Unable to parse header {:?} as a string - {}", + hdr_value, e)), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect())), + Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} into a header - {}", + hdr_value, e)) + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)) + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert: {:?} into a header: {}", + hdr_value, e)) + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert header {:?} to string {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} to a header: {}", + hdr_value, e)), + } + } +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/lib.mustache b/modules/openapi-generator/src/main/resources/rust-axum/lib.mustache new file mode 100644 index 00000000000..ef07e5afa96 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/lib.mustache @@ -0,0 +1,99 @@ +#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = "{{{basePathWithoutHost}}}"; +{{#appVersion}} +pub const API_VERSION: &str = "{{{.}}}"; +{{/appVersion}} + +{{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + {{>response}} + {{/operation}} + {{/operations}} + {{/apis}} +{{/apiInfo}} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { +{{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + + {{#summary}} + /// {{{.}}}. + /// + {{/summary}} + {{#vendorExtensions}} + /// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}} + async fn {{{x-operation-id}}}( + &self, + method: Method, + host: Host, + cookies: CookieJar, + {{#headerParams.size}} + header_params: models::{{{operationIdCamelCase}}}HeaderParams, + {{/headerParams.size}} + {{#pathParams.size}} + path_params: models::{{{operationIdCamelCase}}}PathParams, + {{/pathParams.size}} + {{#queryParams.size}} + query_params: models::{{{operationIdCamelCase}}}QueryParams, + {{/queryParams.size}} + {{^x-consumes-multipart-related}} + {{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + {{^x-consumes-plain-text}} + body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, + {{/x-consumes-plain-text}} + {{#x-consumes-plain-text}} + {{#isString}} + body: String, + {{/isString}} + {{^isString}} + body: Bytes, + {{/isString}} + {{/x-consumes-plain-text}} + {{/vendorExtensions}} + {{/bodyParam}} + {{/x-consumes-multipart}} + {{/x-consumes-multipart-related}} + {{#x-consumes-multipart}} + body: Multipart, + {{/x-consumes-multipart}} + {{#x-consumes-multipart-related}} + body: axum::body::Body, + {{/x-consumes-multipart-related}} + ) -> Result<{{{operationId}}}Response, String>; + {{/vendorExtensions}} + + {{/operation}} + {{/operations}} + {{/apis}} +{{/apiInfo}} +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache new file mode 100644 index 00000000000..00b23629acb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache @@ -0,0 +1,923 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +{{! Don't "use" structs here - they can conflict with the names of models, and mean that the code won't compile }} +{{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + +{{#vendorExtensions}} + {{#headerParams.size}} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + pub struct {{{operationIdCamelCase}}}HeaderParams { + {{#headerParams}} + {{#hasValidation}} + #[validate( + {{#maxLength}} + {{#minLength}} + length(min = {{minLength}}, max = {{maxLength}}), + {{/minLength}} + {{^minLength}} + length(max = {{maxLength}}), + {{/minLength}} + {{/maxLength}} + {{^maxLength}} + {{#minLength}} + length(min = {{minLength}}), + {{/minLength}} + {{/maxLength}} + {{#pattern}} + {{^isByteArray}} + regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}", + {{/isByteArray}} + {{#isByteArray}} + custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.lowercase}}" + {{/isByteArray}} + {{/pattern}} + {{#maximum}} + {{#minimum}} + range(min = {{minimum}}, max = {{maximum}}), + {{/minimum}} + {{^minimum}} + range(max = {{maximum}}), + {{/minimum}} + {{/maximum}} + {{#minimum}} + {{^maximum}} + range(min = {{minimum}}), + {{/maximum}} + {{/minimum}} + {{#maxItems}} + {{#minItems}} + length(min = {{minItems}}, max = {{maxItems}}), + {{/minItems}} + {{^minItems}} + length(max = {{maxItems}}), + {{/minItems}} + {{/maxItems}} + {{^maxItems}} + {{#minItems}} + length(min = {{minItems}}), + {{/minItems}} + {{/maxItems}} + )] + {{/hasValidation}} + pub {{{paramName}}}: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, + {{/headerParams}} + } + + {{#headerParams}} + {{#hasValidation}} + {{#pattern}} + {{^isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap(); + } + {{/isByteArray}} + {{#isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap(); + } + fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.lowercase}}( + b: &ByteArray + ) -> std::result::Result<(), validator::ValidationError> { + if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}HeaderParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) { + return Err(validator::ValidationError::new("Character not allowed")); + } + std::result::Result::Ok(()) + } + {{/isByteArray}} + {{/pattern}} + {{/hasValidation}} + {{/headerParams}} + + {{/headerParams.size}} + {{#pathParams.size}} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + pub struct {{{operationIdCamelCase}}}PathParams { + {{#pathParams}} + {{#description}} + /// {{{.}}} + {{/description}} + {{#isEnum}} + /// Note: inline enums are not fully supported by openapi-generator + {{/isEnum}} + {{#hasValidation}} + #[validate( + {{#maxLength}} + {{#minLength}} + length(min = {{minLength}}, max = {{maxLength}}), + {{/minLength}} + {{^minLength}} + length(max = {{maxLength}}), + {{/minLength}} + {{/maxLength}} + {{^maxLength}} + {{#minLength}} + length(min = {{minLength}}), + {{/minLength}} + {{/maxLength}} + {{#pattern}} + {{^isByteArray}} + regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}", + {{/isByteArray}} + {{#isByteArray}} + custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.lowercase}}" + {{/isByteArray}} + {{/pattern}} + {{#maximum}} + {{#minimum}} + range(min = {{minimum}}, max = {{maximum}}), + {{/minimum}} + {{^minimum}} + range(max = {{maximum}}), + {{/minimum}} + {{/maximum}} + {{#minimum}} + {{^maximum}} + range(min = {{minimum}}), + {{/maximum}} + {{/minimum}} + {{#maxItems}} + {{#minItems}} + length(min = {{minItems}}, max = {{maxItems}}), + {{/minItems}} + {{^minItems}} + length(max = {{maxItems}}), + {{/minItems}} + {{/maxItems}} + {{^maxItems}} + {{#minItems}} + length(min = {{minItems}}), + {{/minItems}} + {{/maxItems}} + )] + {{/hasValidation}} + {{#required}} + pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, + {{/required}} + {{^required}} + {{#isNullable}} + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + {{/isNullable}} + #[serde(skip_serializing_if="Option::is_none")] + pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>, + {{/required}} + {{/pathParams}} + } + + {{#pathParams}} + {{#hasValidation}} + {{#pattern}} + {{^isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap(); + } + {{/isByteArray}} + {{#isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap(); + } + fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.lowercase}}( + b: &ByteArray + ) -> std::result::Result<(), validator::ValidationError> { + if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}PathParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) { + return Err(validator::ValidationError::new("Character not allowed")); + } + std::result::Result::Ok(()) + } + {{/isByteArray}} + {{/pattern}} + {{/hasValidation}} + {{/pathParams}} + + {{/pathParams.size}} + {{#queryParams.size}} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + pub struct {{{operationIdCamelCase}}}QueryParams { + {{#queryParams}} + {{#vendorExtensions}} + {{#description}} + /// {{{.}}} + {{/description}} + {{#isEnum}} + /// Note: inline enums are not fully supported by openapi-generator + {{/isEnum}} + #[serde(rename = "{{{baseName}}}")] + {{#hasValidation}} + #[validate( + {{#maxLength}} + {{#minLength}} + length(min = {{minLength}}, max = {{maxLength}}), + {{/minLength}} + {{^minLength}} + length(max = {{maxLength}}), + {{/minLength}} + {{/maxLength}} + {{^maxLength}} + {{#minLength}} + length(min = {{minLength}}), + {{/minLength}} + {{/maxLength}} + {{#pattern}} + {{^isByteArray}} + regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}", + {{/isByteArray}} + {{#isByteArray}} + custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.lowercase}}" + {{/isByteArray}} + {{/pattern}} + {{#maximum}} + {{#minimum}} + range(min = {{minimum}}, max = {{maximum}}), + {{/minimum}} + {{^minimum}} + range(max = {{maximum}}), + {{/minimum}} + {{/maximum}} + {{#minimum}} + {{^maximum}} + range(min = {{minimum}}), + {{/maximum}} + {{/minimum}} + {{#maxItems}} + {{#minItems}} + length(min = {{minItems}}, max = {{maxItems}}), + {{/minItems}} + {{^minItems}} + length(max = {{maxItems}}), + {{/minItems}} + {{/maxItems}} + {{^maxItems}} + {{#minItems}} + length(min = {{minItems}}), + {{/minItems}} + {{/maxItems}} + )] + {{/hasValidation}} + {{#required}} + pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, + {{/required}} + {{^required}} + {{#isNullable}} + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + {{/isNullable}} + #[serde(skip_serializing_if="Option::is_none")] + pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>, + {{/required}} + {{/vendorExtensions}} + {{/queryParams}} + } + + {{#queryParams}} + {{#hasValidation}} + {{#pattern}} + {{^isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap(); + } + {{/isByteArray}} + {{#isByteArray}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap(); + } + fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.lowercase}}( + b: &ByteArray + ) -> std::result::Result<(), validator::ValidationError> { + if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}QueryParams_{{{paramName}}}{{/lambda.uppercase}}.is_match(&b.0) { + return Err(validator::ValidationError::new("Character not allowed")); + } + std::result::Result::Ok(()) + } + {{/isByteArray}} + {{/pattern}} + {{/hasValidation}} + {{/queryParams}} + {{/queryParams.size}} +{{/vendorExtensions}} + {{/operation}} + {{/operations}} + {{/apis}} +{{/apiInfo}} + +{{! Don't "use" structs here - they can conflict with the names of models, and mean that the code won't compile }} +{{#models}} +{{#model}} + +{{#description}} +/// {{{.}}} +{{/description}} +{{#isEnum}} +/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))] +pub enum {{{classname}}} { +{{#allowableValues}} + {{#enumVars}} + #[serde(rename = {{{value}}})] + {{{name}}}, + {{/enumVars}} +{{/allowableValues}} +} + +impl std::fmt::Display for {{{classname}}} { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { +{{#allowableValues}} + {{#enumVars}} + {{{classname}}}::{{{name}}} => write!(f, {{{value}}}), + {{/enumVars}} +{{/allowableValues}} + } + } +} + +impl std::str::FromStr for {{{classname}}} { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { +{{#allowableValues}} + {{#enumVars}} + {{{value}}} => std::result::Result::Ok({{{classname}}}::{{{name}}}), + {{/enumVars}} +{{/allowableValues}} + _ => std::result::Result::Err(format!("Value not valid: {}", s)), + } + } +} +{{/isEnum}} +{{^isEnum}} +{{#dataType}} +{{#isMap}} +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +{{/isMap}} +{{^isMap}} +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +{{/isMap}} +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct {{{classname}}}({{{dataType}}}); + +impl validator::Validate for {{{classname}}} { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From<{{{dataType}}}> for {{{classname}}} { + fn from(x: {{{dataType}}}) -> Self { + {{{classname}}}(x) + } +} +{{#vendorExtensions.x-is-string}} + +impl std::string::ToString for {{{classname}}} { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for {{{classname}}} { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok({{{classname}}}(x.to_string())) + } +} +{{/vendorExtensions.x-is-string}} + +impl std::convert::From<{{{classname}}}> for {{{dataType}}} { + fn from(x: {{{classname}}}) -> Self { + x.0 + } +} + +impl std::ops::Deref for {{{classname}}} { + type Target = {{{dataType}}}; + fn deref(&self) -> &{{{dataType}}} { + &self.0 + } +} + +impl std::ops::DerefMut for {{{classname}}} { + fn deref_mut(&mut self) -> &mut {{{dataType}}} { + &mut self.0 + } +} + +{{#additionalPropertiesType}} +/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl ::std::string::ToString for {{{classname}}} { + fn to_string(&self) -> String { + // Skipping additionalProperties in query parameter serialization + "".to_string() + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl ::std::str::FromStr for {{{classname}}} { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Err("Parsing additionalProperties for {{{classname}}} is not supported") + } +} +{{/additionalPropertiesType}} +{{/dataType}} +{{^dataType}} +{{#arrayModelType}} +{{! vec}} +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct {{{classname}}}(Vec<{{{arrayModelType}}}>); + +impl validator::Validate for {{{classname}}} { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for {{{classname}}} { + fn from(x: Vec<{{{arrayModelType}}}>) -> Self { + {{{classname}}}(x) + } +} + +impl std::convert::From<{{{classname}}}> for Vec<{{{arrayModelType}}}> { + fn from(x: {{{classname}}}) -> Self { + x.0 + } +} + +impl std::iter::FromIterator<{{{arrayModelType}}}> for {{{classname}}} { + fn from_iter>(u: U) -> Self { + {{{classname}}}(Vec::<{{{arrayModelType}}}>::from_iter(u)) + } +} + +impl std::iter::IntoIterator for {{{classname}}} { + type Item = {{{arrayModelType}}}; + type IntoIter = std::vec::IntoIter<{{{arrayModelType}}}>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a {{{classname}}} { + type Item = &'a {{{arrayModelType}}}; + type IntoIter = std::slice::Iter<'a, {{{arrayModelType}}}>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a mut {{{classname}}} { + type Item = &'a mut {{{arrayModelType}}}; + type IntoIter = std::slice::IterMut<'a, {{{arrayModelType}}}>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl std::ops::Deref for {{{classname}}} { + type Target = Vec<{{{arrayModelType}}}>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for {{{classname}}} { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for {{{classname}}} { + fn to_string(&self) -> String { + self.iter().map(|x| x.to_string()).collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for {{{classname}}} { + type Err = <{{{arrayModelType}}} as std::str::FromStr>::Err; + + fn from_str(s: &str) -> std::result::Result { + let mut items = vec![]; + for item in s.split(',') + { + items.push(item.parse()?); + } + std::result::Result::Ok({{{classname}}}(items)) + } +} + +{{/arrayModelType}} +{{^arrayModelType}} +{{! general struct}} + +{{#anyOf.size}} +/// Any of: +{{#anyOf}} +/// - {{{.}}} +{{/anyOf}} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct {{{classname}}}(Box); + +impl validator::Validate for {{{classname}}} +{ + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for {{{classname}}} { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for {{{classname}}} { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} +{{/anyOf.size}} + +{{#oneOf.size}} +/// One of: +{{#oneOf}} +/// - {{{.}}} +{{/oneOf}} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct {{{classname}}}(Box); + +impl validator::Validate for {{{classname}}} +{ + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for {{{classname}}} { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for {{{classname}}} { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} +{{/oneOf.size}} + +{{^anyOf.size}} +{{^oneOf.size}} +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct {{{classname}}} { +{{#vars}} +{{#description}} +/// {{{.}}} +{{/description}} +{{#isEnum}} +/// Note: inline enums are not fully supported by openapi-generator +{{/isEnum}} + #[serde(rename = "{{{baseName}}}")] +{{#hasValidation}} + #[validate( + {{#maxLength}} + {{#minLength}} + length(min = {{minLength}}, max = {{maxLength}}), + {{/minLength}} + {{^minLength}} + length(max = {{maxLength}}), + {{/minLength}} + {{/maxLength}} + {{^maxLength}} + {{#minLength}} + length(min = {{minLength}}), + {{/minLength}} + {{/maxLength}} + {{#pattern}} + {{^isByteArray}} + regex = "RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}", + {{/isByteArray}} + {{#isByteArray}} + custom ="validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowercase}}" + {{/isByteArray}} + {{/pattern}} + {{#maximum}} + {{#minimum}} + range(min = {{minimum}}, max = {{maximum}}), + {{/minimum}} + {{^minimum}} + range(max = {{maximum}}), + {{/minimum}} + {{/maximum}} + {{#minimum}} + {{^maximum}} + range(min = {{minimum}}), + {{/maximum}} + {{/minimum}} + {{#maxItems}} + {{#minItems}} + length(min = {{minItems}}, max = {{maxItems}}), + {{/minItems}} + {{^minItems}} + length(max = {{maxItems}}), + {{/minItems}} + {{/maxItems}} + {{^maxItems}} + {{#minItems}} + length(min = {{minItems}}), + {{/minItems}} + {{/maxItems}} + )] +{{/hasValidation}} +{{#required}} + pub {{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, +{{/required}} +{{^required}} +{{#isNullable}} + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] +{{/isNullable}} + #[serde(skip_serializing_if="Option::is_none")] + pub {{{name}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>, +{{/required}} + +{{/vars}} +} + +{{#vars}} +{{#hasValidation}} +{{#pattern}} +{{^isByteArray}} +lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap(); +} +{{/isByteArray}} +{{#isByteArray}} +lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap(); +} +fn validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowercase}}( + b: &ByteArray +) -> std::result::Result<(), validator::ValidationError> { + if !RE_{{#lambda.uppercase}}{{{classname}}}_{{{name}}}{{/lambda.uppercase}}.is_match(&b.0) { + return Err(validator::ValidationError::new("Character not allowed")); + } + std::result::Result::Ok(()) +} +{{/isByteArray}} +{{/pattern}} +{{/hasValidation}} +{{/vars}} + +impl {{{classname}}} { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new({{#vars}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/vars}}) -> {{{classname}}} { + {{{classname}}} { +{{#vars}} {{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}}, +{{/vars}} + } + } +} + +/// Converts the {{{classname}}} value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for {{{classname}}} { + fn to_string(&self) -> String { + let params: Vec> = vec![ +{{#vars}} +{{#isByteArray}} + // Skipping {{baseName}} in query parameter serialization +{{/isByteArray}} +{{#isBinary}} + // Skipping {{baseName}} in query parameter serialization +{{/isBinary}} +{{#isMap}} + // Skipping {{baseName}} in query parameter serialization +{{/isMap}} +{{^isPrimitiveType}} + // Skipping {{baseName}} in query parameter serialization +{{/isPrimitiveType}} +{{^isByteArray}}{{^isBinary}}{{^isMap}}{{#isPrimitiveType}} +{{#required}} + Some("{{{baseName}}}".to_string()), +{{^isArray}} +{{#isNullable}} + Some(self.{{{name}}}.as_ref().map_or("null".to_string(), |x| x.to_string())), +{{/isNullable}} +{{^isNullable}} + Some(self.{{{name}}}.to_string()), +{{/isNullable}} +{{/isArray}} +{{#isArray}} +{{#isNullable}} + Some(self.{{{name}}}.as_ref().map_or(vec!["null".to_string()], |x| x.iter().map(|x| x.to_string()).collect::>().join(","))), +{{/isNullable}} +{{^isNullable}} + Some(self.{{{name}}}.iter().map(|x| x.to_string()).collect::>().join(",")), +{{/isNullable}} +{{/isArray}} +{{/required}} +{{^required}} + self.{{{name}}}.as_ref().map(|{{{name}}}| { + [ + "{{{baseName}}}".to_string(), +{{^isArray}} +{{#isNullable}} + {{{name}}}.as_ref().map_or("null".to_string(), |x| x.to_string()), +{{/isNullable}} +{{^isNullable}} + {{{name}}}.to_string(), +{{/isNullable}} +{{/isArray}} +{{#isArray}} +{{#isNullable}} + {{{name}}}.as_ref().map_or("null".to_string(), |x| x.iter().map(|x| x.to_string()).collect::>().join(",")), +{{/isNullable}} +{{^isNullable}} + {{{name}}}.iter().map(|x| x.to_string()).collect::>().join(","), +{{/isNullable}} +{{/isArray}} + ].join(",") + }), +{{/required}} +{{/isPrimitiveType}}{{/isMap}}{{/isBinary}}{{/isByteArray}} +{{/vars}} + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for {{{classname}}} { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + {{#vars}} + pub {{{name}}}: Vec<{{{dataType}}}>, + {{/vars}} + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing {{{classname}}}".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + {{#vars}} + {{#isBinary}} + "{{{baseName}}}" => return std::result::Result::Err("Parsing binary data in this style is not supported in {{{classname}}}".to_string()), + {{/isBinary}} + {{^isBinary}} + {{#isByteArray}} + "{{{baseName}}}" => return std::result::Result::Err("Parsing binary data in this style is not supported in {{{classname}}}".to_string()), + {{/isByteArray}} + {{^isByteArray}} + {{#isContainer}} + "{{{baseName}}}" => return std::result::Result::Err("Parsing a container in this style is not supported in {{{classname}}}".to_string()), + {{/isContainer}} + {{^isContainer}} + {{#isNullable}} + "{{{baseName}}}" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in {{{classname}}}".to_string()), + {{/isNullable}} + {{^isNullable}} + #[allow(clippy::redundant_clone)] + "{{{baseName}}}" => intermediate_rep.{{{name}}}.push(<{{{dataType}}} as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + {{/isNullable}} + {{/isContainer}} + {{/isByteArray}} + {{/isBinary}} + {{/vars}} + _ => return std::result::Result::Err("Unexpected key while parsing {{{classname}}}".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok({{{classname}}} { + {{#vars}} + {{#isNullable}} + {{{name}}}: std::result::Result::Err("Nullable types not supported in {{{classname}}}".to_string())?, + {{/isNullable}} + {{^isNullable}} + {{{name}}}: intermediate_rep.{{{name}}}.into_iter().next(){{#required}}.ok_or_else(|| "{{{baseName}}} missing in {{{classname}}}".to_string())?{{/required}}, + {{/isNullable}} + {{/vars}} + }) + } +} +{{/oneOf.size}} +{{/anyOf.size}} +{{/arrayModelType}} + +{{^anyOf.size}} +{{^oneOf.size}} +// Methods for converting between header::IntoHeaderValue<{{{classname}}}> and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue<{{{classname}}}>) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for {{classname}} - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue<{{{classname}}}> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match <{{{classname}}} as std::str::FromStr>::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into {{classname}} - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +{{/oneOf.size}} +{{/anyOf.size}} + +{{/dataType}} +{{/isEnum}} + +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/response.mustache b/modules/openapi-generator/src/main/resources/rust-axum/response.mustache new file mode 100644 index 00000000000..8b99af9e664 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/response.mustache @@ -0,0 +1,71 @@ +#[derive(Debug, PartialEq, Serialize, Deserialize)] +{{#vendorExtensions.x-must-use-response}} +#[must_use] +#[allow(clippy::large_enum_variant)] +{{/vendorExtensions.x-must-use-response}} +pub enum {{{operationId}}}Response { +{{#responses}} + {{#message}} + /// {{{.}}}{{/message}} + {{#vendorExtensions}} + {{{x-response-id}}} + {{/vendorExtensions}} + {{^dataType}} + {{#hasHeaders}} + { + {{/hasHeaders}} + {{/dataType}} + {{#dataType}} + {{^hasHeaders}} + {{#vendorExtensions}} + {{#x-produces-plain-text}} + (String) + {{/x-produces-plain-text}} + {{#x-produces-bytes}} + (ByteArray) + {{/x-produces-bytes}} + {{^x-produces-plain-text}} + {{^x-produces-bytes}} + ({{{dataType}}}) + {{/x-produces-bytes}} + {{/x-produces-plain-text}} + {{/vendorExtensions}} + {{/hasHeaders}} + {{#hasHeaders}} + { + {{#vendorExtensions}} + {{#x-produces-plain-text}} + body: String, + {{/x-produces-plain-text}} + {{#x-produces-bytes}} + body: ByteArray, + {{/x-produces-bytes}} + {{^x-produces-plain-text}} + {{^x-produces-bytes}} + body: {{{dataType}}}, + {{/x-produces-bytes}} + {{/x-produces-plain-text}} + {{/vendorExtensions}} + {{/hasHeaders}} + {{/dataType}} + {{#headers}} + {{{name}}}: + {{^required}} + Option< + {{/required}} + {{{dataType}}} + {{^required}} + > + {{/required}} + {{^-last}} + , + {{/-last}} + {{#-last}} + } + {{/-last}} + {{/headers}} + {{^-last}} + , + {{/-last}} +{{/responses}} +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache new file mode 100644 index 00000000000..a460237f55d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache @@ -0,0 +1,13 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache new file mode 100644 index 00000000000..69a703ca070 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache @@ -0,0 +1,16 @@ +{{>server-imports}} +use crate::{Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +}; + +{{>server-route}} +{{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} +{{>server-operation-validate}} +{{>server-operation}} + {{/operation}} + {{/operations}} + {{/apis}} +{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-operation-validate.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-operation-validate.mustache new file mode 100644 index 00000000000..6bacec90170 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-operation-validate.mustache @@ -0,0 +1,221 @@ +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + #[derive(validator::Validate)] + #[allow(dead_code)] + struct {{{operationIdCamelCase}}}BodyValidator<'a> { + {{#hasValidation}} + #[validate( + {{#maxLength}} + {{#minLength}} + length(min = {{minLength}}, max = {{maxLength}}), + {{/minLength}} + {{^minLength}} + length(max = {{maxLength}}), + {{/minLength}} + {{/maxLength}} + {{^maxLength}} + {{#minLength}} + length(min = {{minLength}}), + {{/minLength}} + {{/maxLength}} + {{#pattern}} + {{#isString}} + regex = "RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}", + {{/isString}} + {{^isString}} + custom ="validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}", + {{/isString}} + {{/pattern}} + {{#maximum}} + {{#minimum}} + range(min = {{minimum}}, max = {{maximum}}), + {{/minimum}} + {{^minimum}} + range(max = {{maximum}}), + {{/minimum}} + {{/maximum}} + {{#minimum}} + {{^maximum}} + range(min = {{minimum}}), + {{/maximum}} + {{/minimum}} + {{#maxItems}} + {{#minItems}} + length(min = {{minItems}}, max = {{maxItems}}), + {{/minItems}} + {{^minItems}} + length(max = {{maxItems}}), + {{/minItems}} + {{/maxItems}} + {{^maxItems}} + {{#minItems}} + length(min = {{minItems}}), + {{/minItems}} + {{/maxItems}} + )] + {{/hasValidation}} + {{^x-consumes-plain-text}} + {{^hasValidation}} + {{^isMap}} + #[validate] + {{/isMap}} + {{/hasValidation}} + body: &'a {{{dataType}}}, + {{/x-consumes-plain-text}} + {{#x-consumes-plain-text}} + {{#isString}} + body: &'a String, + {{/isString}} + {{^isString}} + body: &'a [u8], + {{/isString}} + {{/x-consumes-plain-text}} + } + + {{#hasValidation}} + {{#pattern}} + {{#isString}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}: regex::Regex = regex::Regex::new(r"{{ pattern }}").unwrap(); + } + {{/isString}} + {{^isString}} + lazy_static::lazy_static! { + static ref RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}: regex::bytes::Regex = regex::bytes::Regex::new(r"{{ pattern }}").unwrap(); + } + fn validate_byte_{{#lambda.lowercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.lowercase}}( + b: &[u8] + ) -> std::result::Result<(), validator::ValidationError> { + if !RE_{{#lambda.uppercase}}{{{operationIdCamelCase}}}BodyValidator{{/lambda.uppercase}}.is_match(b) { + return Err(validator::ValidationError::new("Character not allowed")); + } + Ok(()) + } + {{/isString}} + {{/pattern}} + {{/hasValidation}} + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} + +#[tracing::instrument(skip_all)] +fn {{#vendorExtensions}}{{{x-operation-id}}}_validation{{/vendorExtensions}}( +{{#headerParams.size}} + header_params: models::{{{operationIdCamelCase}}}HeaderParams, +{{/headerParams.size}} +{{#pathParams.size}} + path_params: models::{{{operationIdCamelCase}}}PathParams, +{{/pathParams.size}} +{{#queryParams.size}} + query_params: models::{{{operationIdCamelCase}}}QueryParams, +{{/queryParams.size}} +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + {{^x-consumes-plain-text}} + body: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, + {{/x-consumes-plain-text}} + {{#x-consumes-plain-text}} + {{#isString}} + body: String, + {{/isString}} + {{^isString}} + body: Bytes, + {{/isString}} + {{/x-consumes-plain-text}} + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} +) -> std::result::Result<( +{{#headerParams.size}} + models::{{{operationIdCamelCase}}}HeaderParams, +{{/headerParams.size}} +{{#pathParams.size}} + models::{{{operationIdCamelCase}}}PathParams, +{{/pathParams.size}} +{{#queryParams.size}} + models::{{{operationIdCamelCase}}}QueryParams, +{{/queryParams.size}} +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + {{^x-consumes-plain-text}} + {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, + {{/x-consumes-plain-text}} + {{#x-consumes-plain-text}} + {{#isString}} + String, + {{/isString}} + {{^isString}} + Bytes, + {{/isString}} + {{/x-consumes-plain-text}} + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} +), ValidationErrors> +{ +{{#headerParams.size}} + header_params.validate()?; +{{/headerParams.size}} +{{#pathParams.size}} + path_params.validate()?; +{{/pathParams.size}} +{{#queryParams.size}} + query_params.validate()?; +{{/queryParams.size}} +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + {{^x-consumes-plain-text}} + {{#required}} + let b = {{{operationIdCamelCase}}}BodyValidator { body: &body }; + b.validate()?; + {{/required}} + {{^required}} + if let Some(body) = &body { + let b = {{{operationIdCamelCase}}}BodyValidator { body }; + b.validate()?; + } + {{/required}} + {{/x-consumes-plain-text}} + {{#x-consumes-plain-text}} + {{#hasValidation}} + let b = {{{operationIdCamelCase}}}BodyValidator { body: &body }; + b.validate()?; + {{/hasValidation}} + {{/x-consumes-plain-text}} + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} + +Ok(( +{{#headerParams.size}} + header_params, +{{/headerParams.size}} +{{#pathParams.size}} + path_params, +{{/pathParams.size}} +{{#queryParams.size}} + query_params, +{{/queryParams.size}} +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + body, + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} +)) +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache new file mode 100644 index 00000000000..5ed29495424 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache @@ -0,0 +1,309 @@ +/// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}} +#[tracing::instrument(skip_all)] +async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}( + method: Method, + host: Host, + cookies: CookieJar, +{{#headerParams.size}} + headers: HeaderMap, +{{/headerParams.size}} +{{#pathParams.size}} + Path(path_params): Path, +{{/pathParams.size}} +{{#queryParams.size}} + Query(query_params): Query, +{{/queryParams.size}} + State(api_impl): State, +{{#vendorExtensions}} +{{^x-consumes-multipart-related}} +{{^x-consumes-multipart}} + {{#bodyParam}} + {{#vendorExtensions}} + {{#x-consumes-json}} + Json(body): Json<{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}>, + {{/x-consumes-json}} + {{#x-consumes-form-urlencoded}} + Form(body): Form<{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}>, + {{/x-consumes-form-urlencoded}} + {{#x-consumes-plain-text}} + {{#isString}} + body: String, + {{/isString}} + {{^isString}} + body: Bytes, + {{/isString}} + {{/x-consumes-plain-text}} + {{/vendorExtensions}} + {{/bodyParam}} +{{/x-consumes-multipart}} +{{/x-consumes-multipart-related}} +{{#x-consumes-multipart}} + body: Multipart, +{{/x-consumes-multipart}} +{{#x-consumes-multipart-related}} + body: axum::body::Body, +{{/x-consumes-multipart-related}} +{{/vendorExtensions}} +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ +{{#headerParams}} + {{#-first}} + // Header parameters + let header_params = { + {{/-first}} + let header_{{{paramName}}} = headers.get(HeaderName::from_static("{{{nameInLowerCase}}}")); + + let header_{{{paramName}}} = match header_{{{paramName}}} { + Some(v) => match header::IntoHeaderValue::<{{{dataType}}}>::try_from((*v).clone()) { + Ok(result) => +{{#required}} + result.0, +{{/required}} +{{^required}} + Some(result.0), +{{/required}} + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Invalid header {{{baseName}}} - {}", err))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + + }, + }, + None => { +{{#required}} + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from("Missing required header {{{baseName}}}")).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); +{{/required}} +{{^required}} + None +{{/required}} + } + }; +{{/headerParams}} +{{#headerParams}} + {{#-first}} + + models::{{{operationIdCamelCase}}}HeaderParams { + {{/-first}} + {{{paramName}}}: header_{{{paramName}}}, + {{#-last}} + } + }; + + {{/-last}} +{{/headerParams}} + + {{^disableValidator}} + {{^allowBlockingValidator}} + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + {{/allowBlockingValidator}} + {{#allowBlockingValidator}} + let validation = + {{/allowBlockingValidator}} + {{#vendorExtensions}}{{{x-operation-id}}}_validation{{/vendorExtensions}}( + {{#headerParams.size}} + header_params, + {{/headerParams.size}} + {{#pathParams.size}} + path_params, + {{/pathParams.size}} + {{#queryParams.size}} + query_params, + {{/queryParams.size}} + {{^x-consumes-multipart-related}} + {{^x-consumes-multipart}} + {{#bodyParam}} + body, + {{/bodyParam}} + {{/x-consumes-multipart}} + {{/x-consumes-multipart-related}} + ) + {{^allowBlockingValidator}}).await.unwrap(){{/allowBlockingValidator}}; + + let Ok(( + {{#headerParams.size}} + header_params, + {{/headerParams.size}} + {{#pathParams.size}} + path_params, + {{/pathParams.size}} + {{#queryParams.size}} + query_params, + {{/queryParams.size}} + {{^x-consumes-multipart-related}} + {{^x-consumes-multipart}} + {{#bodyParam}} + body, + {{/bodyParam}} + {{/x-consumes-multipart}} + {{/x-consumes-multipart-related}} + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + {{/disableValidator}} + + let result = api_impl.as_ref().{{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}( + method, + host, + cookies, + {{#headerParams.size}} + header_params, + {{/headerParams.size}} + {{#pathParams.size}} + path_params, + {{/pathParams.size}} + {{#queryParams.size}} + query_params, + {{/queryParams.size}} + {{#vendorExtensions}} + {{^x-consumes-multipart-related}} + {{^x-consumes-multipart}} + {{#bodyParams}} + {{#-first}} + body, + {{/-first}} + {{/bodyParams}} + {{/x-consumes-multipart}} + {{/x-consumes-multipart-related}} + {{#x-consumes-multipart}} + body, + {{/x-consumes-multipart}} + {{#x-consumes-multipart-related}} + body, + {{/x-consumes-multipart-related}} + {{/vendorExtensions}} + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { +{{#responses}} + {{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}} +{{#dataType}} +{{^headers}} + (body) +{{/headers}} +{{#headers}} +{{#-first}} + { + body, +{{/-first}} + {{{name}}}{{^-last}},{{/-last}} +{{#-last}} + } +{{/-last}} +{{/headers}} +{{/dataType}} +{{^dataType}} +{{#headers}} +{{#-first}} + { +{{/-first}} + {{{name}}}{{^-last}},{{/-last}} +{{#-last}} + } +{{/-last}} +{{/headers}} +{{/dataType}} + => { +{{#headers}} + {{^required}} + if let Some({{{name}}}) = {{{name}}} { + {{/required}} + let {{{name}}} = match header::IntoHeaderValue({{{name}}}).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling {{name}} header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + HeaderName::from_static("{{{nameInLowerCase}}}"), + {{name}} + ); + } + {{^required}} + } + {{/required}} +{{/headers}} + + let mut response = response.status({{{code}}}); +{{#produces}} +{{#-first}} +{{#dataType}} +{{#vendorExtensions}} + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("{{{x-mime-type}}}").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?); + } + +{{/vendorExtensions}} +{{/dataType}} +{{/-first}} +{{/produces}} +{{#dataType}} +{{#vendorExtensions}} +{{#x-produces-json}} + {{^allowBlockingResponseSerialize}} + let body_content = tokio::task::spawn_blocking(move || + {{/allowBlockingResponseSerialize}} + {{#allowBlockingResponseSerialize}} + let body_content = + {{/allowBlockingResponseSerialize}} + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?; +{{/x-produces-json}} +{{#x-produces-form-urlencoded}} + {{^allowBlockingResponseSerialize}} + let body_content = tokio::task::spawn_blocking(move || + {{/allowBlockingResponseSerialize}} + {{#allowBlockingResponseSerialize}} + let body_content = + {{/allowBlockingResponseSerialize}} + serde_urlencoded::to_string(body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?; +{{/x-produces-form-urlencoded}} +{{#x-produces-bytes}} + let body_content = body.0; +{{/x-produces-bytes}} +{{#x-produces-plain-text}} + let body_content = body; +{{/x-produces-plain-text}} +{{/vendorExtensions}} + response.body(Body::from(body_content)) +{{/dataType}} +{{^dataType}} + response.body(Body::empty()) +{{/dataType}} + }, +{{/responses}} + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache new file mode 100644 index 00000000000..2a17022c2c0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache @@ -0,0 +1,15 @@ +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + {{#pathMethodOps}} + .route("{{{basePathWithoutHost}}}{{{path}}}", + {{#methodOperations}}{{{method}}}({{{operationID}}}::){{^-last}}.{{/-last}}{{/methodOperations}} + ) + {{/pathMethodOps}} + .with_state(api_impl) +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/types.mustache b/modules/openapi-generator/src/main/resources/rust-axum/types.mustache new file mode 100644 index 00000000000..1a8e07ef231 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/types.mustache @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use {{{externCrateName}}}::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/.gitignore b/samples/server/petstore/rust-axum/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/Cargo.toml b/samples/server/petstore/rust-axum/Cargo.toml new file mode 100644 index 00000000000..c086d1f0138 --- /dev/null +++ b/samples/server/petstore/rust-axum/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["output/*"] +resolver = "2" diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/.gitignore b/samples/server/petstore/rust-axum/output/multipart-v3/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml new file mode 100644 index 00000000000..9b554d7ce5f --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "multipart-v3" +version = "1.0.7" +authors = ["OpenAPI Generator team and contributors"] +description = "API under test" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/README.md b/samples/server/petstore/rust-axum/output/multipart-v3/README.md new file mode 100644 index 00000000000..d7e08f99cb3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/README.md @@ -0,0 +1,91 @@ +# Rust API for multipart-v3 + +API under test + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0.7 + + + + +This autogenerated project defines an API crate `multipart-v3` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl multipart-v3::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = multipart-v3::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/lib.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/lib.rs new file mode 100644 index 00000000000..2e8961aebe0 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/lib.rs @@ -0,0 +1,82 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "1.0.7"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MultipartRelatedRequestPostResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MultipartRequestPostResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MultipleIdenticalMimeTypesPostResponse { + /// OK + OK, +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// MultipartRelatedRequestPost - POST /multipart_related_request + async fn multipart_related_request_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: axum::body::Body, + ) -> Result; + + /// MultipartRequestPost - POST /multipart_request + async fn multipart_request_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Multipart, + ) -> Result; + + /// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types + async fn multiple_identical_mime_types_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: axum::body::Body, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs new file mode 100644 index 00000000000..56ed14cbe8a --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs @@ -0,0 +1,446 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MultipartRelatedRequest { + #[serde(rename = "object_field")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_field: Option, + + #[serde(rename = "optional_binary_field")] + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_binary_field: Option, + + #[serde(rename = "required_binary_field")] + pub required_binary_field: ByteArray, +} + +impl MultipartRelatedRequest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_binary_field: ByteArray) -> MultipartRelatedRequest { + MultipartRelatedRequest { + object_field: None, + optional_binary_field: None, + required_binary_field, + } + } +} + +/// Converts the MultipartRelatedRequest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MultipartRelatedRequest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping object_field in query parameter serialization + + // Skipping optional_binary_field in query parameter serialization + // Skipping optional_binary_field in query parameter serialization + + // Skipping required_binary_field in query parameter serialization + // Skipping required_binary_field in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MultipartRelatedRequest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MultipartRelatedRequest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub object_field: Vec, + pub optional_binary_field: Vec, + pub required_binary_field: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing MultipartRelatedRequest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "object_field" => intermediate_rep.object_field.push(::from_str(val).map_err(|x| x.to_string())?), + "optional_binary_field" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipartRelatedRequest".to_string()), + "required_binary_field" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipartRelatedRequest".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing MultipartRelatedRequest".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MultipartRelatedRequest { + object_field: intermediate_rep.object_field.into_iter().next(), + optional_binary_field: intermediate_rep.optional_binary_field.into_iter().next(), + required_binary_field: intermediate_rep + .required_binary_field + .into_iter() + .next() + .ok_or_else(|| { + "required_binary_field missing in MultipartRelatedRequest".to_string() + })?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for MultipartRelatedRequest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into MultipartRelatedRequest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MultipartRequestObjectField { + #[serde(rename = "field_a")] + pub field_a: String, + + #[serde(rename = "field_b")] + #[serde(skip_serializing_if = "Option::is_none")] + pub field_b: Option>, +} + +impl MultipartRequestObjectField { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(field_a: String) -> MultipartRequestObjectField { + MultipartRequestObjectField { + field_a, + field_b: None, + } + } +} + +/// Converts the MultipartRequestObjectField value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MultipartRequestObjectField { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("field_a".to_string()), + Some(self.field_a.to_string()), + self.field_b.as_ref().map(|field_b| { + [ + "field_b".to_string(), + field_b + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MultipartRequestObjectField value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MultipartRequestObjectField { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub field_a: Vec, + pub field_b: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing MultipartRequestObjectField".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "field_a" => intermediate_rep.field_a.push(::from_str(val).map_err(|x| x.to_string())?), + "field_b" => return std::result::Result::Err("Parsing a container in this style is not supported in MultipartRequestObjectField".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing MultipartRequestObjectField".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MultipartRequestObjectField { + field_a: intermediate_rep + .field_a + .into_iter() + .next() + .ok_or_else(|| "field_a missing in MultipartRequestObjectField".to_string())?, + field_b: intermediate_rep.field_b.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for MultipartRequestObjectField - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into MultipartRequestObjectField - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MultipleIdenticalMimeTypesPostRequest { + #[serde(rename = "binary1")] + #[serde(skip_serializing_if = "Option::is_none")] + pub binary1: Option, + + #[serde(rename = "binary2")] + #[serde(skip_serializing_if = "Option::is_none")] + pub binary2: Option, +} + +impl MultipleIdenticalMimeTypesPostRequest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> MultipleIdenticalMimeTypesPostRequest { + MultipleIdenticalMimeTypesPostRequest { + binary1: None, + binary2: None, + } + } +} + +/// Converts the MultipleIdenticalMimeTypesPostRequest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MultipleIdenticalMimeTypesPostRequest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping binary1 in query parameter serialization + // Skipping binary1 in query parameter serialization + + // Skipping binary2 in query parameter serialization + // Skipping binary2 in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MultipleIdenticalMimeTypesPostRequest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MultipleIdenticalMimeTypesPostRequest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub binary1: Vec, + pub binary2: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing MultipleIdenticalMimeTypesPostRequest" + .to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "binary1" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipleIdenticalMimeTypesPostRequest".to_string()), + "binary2" => return std::result::Result::Err("Parsing binary data in this style is not supported in MultipleIdenticalMimeTypesPostRequest".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing MultipleIdenticalMimeTypesPostRequest".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MultipleIdenticalMimeTypesPostRequest { + binary1: intermediate_rep.binary1.into_iter().next(), + binary2: intermediate_rep.binary2.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> + for HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for MultipleIdenticalMimeTypesPostRequest - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into MultipleIdenticalMimeTypesPostRequest - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/server/mod.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/server/mod.rs new file mode 100644 index 00000000000..b62bafb87c1 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/server/mod.rs @@ -0,0 +1,211 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{ + Api, MultipartRelatedRequestPostResponse, MultipartRequestPostResponse, + MultipleIdenticalMimeTypesPostResponse, +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route( + "/multipart_related_request", + post(multipart_related_request_post::), + ) + .route("/multipart_request", post(multipart_request_post::)) + .route( + "/multiple-identical-mime-types", + post(multiple_identical_mime_types_post::), + ) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn multipart_related_request_post_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MultipartRelatedRequestPost - POST /multipart_related_request +#[tracing::instrument(skip_all)] +async fn multipart_related_request_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: axum::body::Body, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || multipart_related_request_post_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .multipart_related_request_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MultipartRelatedRequestPostResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn multipart_request_post_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MultipartRequestPost - POST /multipart_request +#[tracing::instrument(skip_all)] +async fn multipart_request_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Multipart, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || multipart_request_post_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .multipart_request_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MultipartRequestPostResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn multiple_identical_mime_types_post_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types +#[tracing::instrument(skip_all)] +async fn multiple_identical_mime_types_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: axum::body::Body, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || multiple_identical_mime_types_post_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .multiple_identical_mime_types_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MultipleIdenticalMimeTypesPostResponse::OK => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs new file mode 100644 index 00000000000..17faecc35e6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use multipart_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use multipart_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use multipart_v3::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/.gitignore b/samples/server/petstore/rust-axum/output/openapi-v3/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml new file mode 100644 index 00000000000..0c2cd974058 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "openapi-v3" +version = "1.0.7" +authors = ["OpenAPI Generator team and contributors"] +description = "API under test" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/README.md b/samples/server/petstore/rust-axum/output/openapi-v3/README.md new file mode 100644 index 00000000000..7343a6f0ef3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/README.md @@ -0,0 +1,91 @@ +# Rust API for openapi-v3 + +API under test + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0.7 + + + + +This autogenerated project defines an API crate `openapi-v3` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl openapi-v3::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = openapi-v3::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/lib.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/lib.rs new file mode 100644 index 00000000000..d69c7d765ec --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/lib.rs @@ -0,0 +1,482 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "1.0.7"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum AnyOfGetResponse { + /// Success + Success(models::AnyOfObject), + /// AlternateSuccess + AlternateSuccess(models::Model12345AnyOfObject), + /// AnyOfSuccess + AnyOfSuccess(models::AnyOfGet202Response), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CallbackWithHeaderPostResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum ComplexQueryParamGetResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum EnumInPathPathParamGetResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum JsonComplexQueryParamGetResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MandatoryRequestHeaderGetResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MergePatchJsonGetResponse { + /// merge-patch+json-encoded response + Merge(models::AnotherXmlObject), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum MultigetGetResponse { + /// JSON rsp + JSONRsp(models::AnotherXmlObject), + /// XML rsp + XMLRsp(String), + /// octet rsp + OctetRsp(ByteArray), + /// string rsp + StringRsp(String), + /// Duplicate Response long text. One. + DuplicateResponseLongText(models::AnotherXmlObject), + /// Duplicate Response long text. Two. + DuplicateResponseLongText_2(models::AnotherXmlObject), + /// Duplicate Response long text. Three. + DuplicateResponseLongText_3(models::AnotherXmlObject), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum MultipleAuthSchemeGetResponse { + /// Check that limiting to multiple required auth schemes works + CheckThatLimitingToMultipleRequiredAuthSchemesWorks, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum OneOfGetResponse { + /// Success + Success(models::OneOfGet200Response), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum OverrideServerGetResponse { + /// Success. + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum ParamgetGetResponse { + /// JSON rsp + JSONRsp(models::AnotherXmlObject), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum ReadonlyAuthSchemeGetResponse { + /// Check that limiting to a single required auth scheme works + CheckThatLimitingToASingleRequiredAuthSchemeWorks, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum RegisterCallbackPostResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum RequiredOctetStreamPutResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum ResponsesWithHeadersGetResponse { + /// Success + Success { + body: String, + success_info: String, + bool_header: Option, + object_header: Option, + }, + /// Precondition Failed + PreconditionFailed { + further_info: Option, + failure_info: Option, + }, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum Rfc7807GetResponse { + /// OK + OK(models::ObjectWithArrayOfObjects), + /// NotFound + NotFound(models::ObjectWithArrayOfObjects), + /// NotAcceptable + NotAcceptable(String), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UntypedPropertyGetResponse { + /// Check that untyped properties works + CheckThatUntypedPropertiesWorks, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UuidGetResponse { + /// Duplicate Response long text. One. + DuplicateResponseLongText(uuid::Uuid), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum XmlExtraPostResponse { + /// OK + OK, + /// Bad Request + BadRequest, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum XmlOtherPostResponse { + /// OK + OK(String), + /// Bad Request + BadRequest, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum XmlOtherPutResponse { + /// OK + OK, + /// Bad Request + BadRequest, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum XmlPostResponse { + /// OK + OK, + /// Bad Request + BadRequest, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum XmlPutResponse { + /// OK + OK, + /// Bad Request + BadRequest, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateRepoResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GetRepoInfoResponse { + /// OK + OK(String), +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// AnyOfGet - GET /any-of + async fn any_of_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::AnyOfGetQueryParams, + ) -> Result; + + /// CallbackWithHeaderPost - POST /callback-with-header + async fn callback_with_header_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::CallbackWithHeaderPostQueryParams, + ) -> Result; + + /// ComplexQueryParamGet - GET /complex-query-param + async fn complex_query_param_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::ComplexQueryParamGetQueryParams, + ) -> Result; + + /// EnumInPathPathParamGet - GET /enum_in_path/{path_param} + async fn enum_in_path_path_param_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::EnumInPathPathParamGetPathParams, + ) -> Result; + + /// JsonComplexQueryParamGet - GET /json-complex-query-param + async fn json_complex_query_param_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::JsonComplexQueryParamGetQueryParams, + ) -> Result; + + /// MandatoryRequestHeaderGet - GET /mandatory-request-header + async fn mandatory_request_header_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + header_params: models::MandatoryRequestHeaderGetHeaderParams, + ) -> Result; + + /// MergePatchJsonGet - GET /merge-patch-json + async fn merge_patch_json_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Get some stuff.. + /// + /// MultigetGet - GET /multiget + async fn multiget_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// MultipleAuthSchemeGet - GET /multiple_auth_scheme + async fn multiple_auth_scheme_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// OneOfGet - GET /one-of + async fn one_of_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// OverrideServerGet - GET /override-server + async fn override_server_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Get some stuff with parameters.. + /// + /// ParamgetGet - GET /paramget + async fn paramget_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::ParamgetGetQueryParams, + ) -> Result; + + /// ReadonlyAuthSchemeGet - GET /readonly_auth_scheme + async fn readonly_auth_scheme_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// RegisterCallbackPost - POST /register-callback + async fn register_callback_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::RegisterCallbackPostQueryParams, + ) -> Result; + + /// RequiredOctetStreamPut - PUT /required_octet_stream + async fn required_octet_stream_put( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// ResponsesWithHeadersGet - GET /responses_with_headers + async fn responses_with_headers_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Rfc7807Get - GET /rfc7807 + async fn rfc7807_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// UntypedPropertyGet - GET /untyped_property + async fn untyped_property_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Option, + ) -> Result; + + /// UuidGet - GET /uuid + async fn uuid_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// XmlExtraPost - POST /xml_extra + async fn xml_extra_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// XmlOtherPost - POST /xml_other + async fn xml_other_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// XmlOtherPut - PUT /xml_other + async fn xml_other_put( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// Post an array. + /// + /// XmlPost - POST /xml + async fn xml_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// XmlPut - PUT /xml + async fn xml_put( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Bytes, + ) -> Result; + + /// CreateRepo - POST /repos + async fn create_repo( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::ObjectParam, + ) -> Result; + + /// GetRepoInfo - GET /repos/{repoId} + async fn get_repo_info( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetRepoInfoPathParams, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs new file mode 100644 index 00000000000..9c95d11a9e3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs @@ -0,0 +1,2878 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnyOfGetQueryParams { + /// list of any of objects + #[serde(rename = "any-of")] + #[validate(length(min = 1))] + #[serde(skip_serializing_if = "Option::is_none")] + pub any_of: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct CallbackWithHeaderPostQueryParams { + #[serde(rename = "url")] + pub url: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ComplexQueryParamGetQueryParams { + #[serde(rename = "list-of-strings")] + #[serde(skip_serializing_if = "Option::is_none")] + pub list_of_strings: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct EnumInPathPathParamGetPathParams { + pub path_param: models::StringEnum, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct JsonComplexQueryParamGetQueryParams { + #[serde(rename = "list-of-strings")] + #[serde(skip_serializing_if = "Option::is_none")] + pub list_of_strings: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MandatoryRequestHeaderGetHeaderParams { + pub x_header: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ParamgetGetQueryParams { + /// The stuff to get + #[serde(rename = "uuid")] + #[serde(skip_serializing_if = "Option::is_none")] + pub uuid: Option, + /// Some object to pass as query parameter + #[serde(rename = "someObject")] + #[serde(skip_serializing_if = "Option::is_none")] + pub some_object: Option, + /// Some list to pass as query parameter + #[serde(rename = "someList")] + #[serde(skip_serializing_if = "Option::is_none")] + pub some_list: Option>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegisterCallbackPostQueryParams { + #[serde(rename = "url")] + pub url: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetRepoInfoPathParams { + pub repo_id: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AdditionalPropertiesWithList(std::collections::HashMap>); + +impl validator::Validate for AdditionalPropertiesWithList { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From>> + for AdditionalPropertiesWithList +{ + fn from(x: std::collections::HashMap>) -> Self { + AdditionalPropertiesWithList(x) + } +} + +impl std::convert::From + for std::collections::HashMap> +{ + fn from(x: AdditionalPropertiesWithList) -> Self { + x.0 + } +} + +impl std::ops::Deref for AdditionalPropertiesWithList { + type Target = std::collections::HashMap>; + fn deref(&self) -> &std::collections::HashMap> { + &self.0 + } +} + +impl std::ops::DerefMut for AdditionalPropertiesWithList { + fn deref_mut(&mut self) -> &mut std::collections::HashMap> { + &mut self.0 + } +} + +/// Converts the AdditionalPropertiesWithList value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl ::std::string::ToString for AdditionalPropertiesWithList { + fn to_string(&self) -> String { + // Skipping additionalProperties in query parameter serialization + "".to_string() + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AdditionalPropertiesWithList value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl ::std::str::FromStr for AdditionalPropertiesWithList { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Err( + "Parsing additionalProperties for AdditionalPropertiesWithList is not supported", + ) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnotherXmlArray(Vec); + +impl validator::Validate for AnotherXmlArray { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for AnotherXmlArray { + fn from(x: Vec) -> Self { + AnotherXmlArray(x) + } +} + +impl std::convert::From for Vec { + fn from(x: AnotherXmlArray) -> Self { + x.0 + } +} + +impl std::iter::FromIterator for AnotherXmlArray { + fn from_iter>(u: U) -> Self { + AnotherXmlArray(Vec::::from_iter(u)) + } +} + +impl std::iter::IntoIterator for AnotherXmlArray { + type Item = String; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a AnotherXmlArray { + type Item = &'a String; + type IntoIter = std::slice::Iter<'a, String>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a mut AnotherXmlArray { + type Item = &'a mut String; + type IntoIter = std::slice::IterMut<'a, String>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl std::ops::Deref for AnotherXmlArray { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for AnotherXmlArray { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Converts the AnotherXmlArray value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnotherXmlArray { + fn to_string(&self) -> String { + self.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnotherXmlArray value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnotherXmlArray { + type Err = ::Err; + + fn from_str(s: &str) -> std::result::Result { + let mut items = vec![]; + for item in s.split(',') { + items.push(item.parse()?); + } + std::result::Result::Ok(AnotherXmlArray(items)) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnotherXmlArray - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnotherXmlArray - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnotherXmlInner(String); + +impl validator::Validate for AnotherXmlInner { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for AnotherXmlInner { + fn from(x: String) -> Self { + AnotherXmlInner(x) + } +} + +impl std::string::ToString for AnotherXmlInner { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for AnotherXmlInner { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(AnotherXmlInner(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: AnotherXmlInner) -> Self { + x.0 + } +} + +impl std::ops::Deref for AnotherXmlInner { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for AnotherXmlInner { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// An XML object + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnotherXmlObject { + #[serde(rename = "inner_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub inner_string: Option, +} + +impl AnotherXmlObject { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnotherXmlObject { + AnotherXmlObject { inner_string: None } + } +} + +/// Converts the AnotherXmlObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnotherXmlObject { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .inner_string + .as_ref() + .map(|inner_string| ["inner_string".to_string(), inner_string.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnotherXmlObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnotherXmlObject { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inner_string: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AnotherXmlObject".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "inner_string" => intermediate_rep.inner_string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AnotherXmlObject".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnotherXmlObject { + inner_string: intermediate_rep.inner_string.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnotherXmlObject - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnotherXmlObject - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Any of: +/// - String +/// - uuid::Uuid +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct AnyOfGet202Response(Box); + +impl validator::Validate for AnyOfGet202Response { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnyOfGet202Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnyOfGet202Response { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for AnyOfGet202Response { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} + +/// Test a model containing an anyOf + +/// Any of: +/// - String +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct AnyOfObject(Box); + +impl validator::Validate for AnyOfObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnyOfObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnyOfObject { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for AnyOfObject { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} + +/// Test containing an anyOf object + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnyOfProperty { + #[serde(rename = "requiredAnyOf")] + pub required_any_of: models::AnyOfObject, + + #[serde(rename = "optionalAnyOf")] + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_any_of: Option, +} + +impl AnyOfProperty { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_any_of: models::AnyOfObject) -> AnyOfProperty { + AnyOfProperty { + required_any_of, + optional_any_of: None, + } + } +} + +/// Converts the AnyOfProperty value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnyOfProperty { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping requiredAnyOf in query parameter serialization + + // Skipping optionalAnyOf in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnyOfProperty value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnyOfProperty { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub required_any_of: Vec, + pub optional_any_of: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AnyOfProperty".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "requiredAnyOf" => intermediate_rep.required_any_of.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "optionalAnyOf" => intermediate_rep.optional_any_of.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AnyOfProperty".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnyOfProperty { + required_any_of: intermediate_rep + .required_any_of + .into_iter() + .next() + .ok_or_else(|| "requiredAnyOf missing in AnyOfProperty".to_string())?, + optional_any_of: intermediate_rep.optional_any_of.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnyOfProperty - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnyOfProperty - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// An XML object + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DuplicateXmlObject { + #[serde(rename = "inner_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub inner_string: Option, + + #[serde(rename = "inner_array")] + pub inner_array: models::XmlArray, +} + +impl DuplicateXmlObject { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(inner_array: models::XmlArray) -> DuplicateXmlObject { + DuplicateXmlObject { + inner_string: None, + inner_array, + } + } +} + +/// Converts the DuplicateXmlObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for DuplicateXmlObject { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.inner_string.as_ref().map(|inner_string| { + ["inner_string".to_string(), inner_string.to_string()].join(",") + }), + // Skipping inner_array in query parameter serialization + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a DuplicateXmlObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for DuplicateXmlObject { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inner_string: Vec, + pub inner_array: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing DuplicateXmlObject".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "inner_string" => intermediate_rep.inner_string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "inner_array" => intermediate_rep.inner_array.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing DuplicateXmlObject".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(DuplicateXmlObject { + inner_string: intermediate_rep.inner_string.into_iter().next(), + inner_array: intermediate_rep + .inner_array + .into_iter() + .next() + .ok_or_else(|| "inner_array missing in DuplicateXmlObject".to_string())?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for DuplicateXmlObject - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into DuplicateXmlObject - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Test a model containing a special character in the enum +/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))] +pub enum EnumWithStarObject { + #[serde(rename = "FOO")] + Foo, + #[serde(rename = "BAR")] + Bar, + #[serde(rename = "*")] + Star, +} + +impl std::fmt::Display for EnumWithStarObject { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + EnumWithStarObject::Foo => write!(f, "FOO"), + EnumWithStarObject::Bar => write!(f, "BAR"), + EnumWithStarObject::Star => write!(f, "*"), + } + } +} + +impl std::str::FromStr for EnumWithStarObject { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + "FOO" => std::result::Result::Ok(EnumWithStarObject::Foo), + "BAR" => std::result::Result::Ok(EnumWithStarObject::Bar), + "*" => std::result::Result::Ok(EnumWithStarObject::Star), + _ => std::result::Result::Err(format!("Value not valid: {}", s)), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Err(String); + +impl validator::Validate for Err { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for Err { + fn from(x: String) -> Self { + Err(x) + } +} + +impl std::string::ToString for Err { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for Err { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(Err(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: Err) -> Self { + x.0 + } +} + +impl std::ops::Deref for Err { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for Err { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Error(String); + +impl validator::Validate for Error { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for Error { + fn from(x: String) -> Self { + Error(x) + } +} + +impl std::string::ToString for Error { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for Error { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(Error(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: Error) -> Self { + x.0 + } +} + +impl std::ops::Deref for Error { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for Error { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// Test a model containing an anyOf that starts with a number + +/// Any of: +/// - String +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct Model12345AnyOfObject(Box); + +impl validator::Validate for Model12345AnyOfObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Model12345AnyOfObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Model12345AnyOfObject { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for Model12345AnyOfObject { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MultigetGet201Response { + #[serde(rename = "foo")] + #[serde(skip_serializing_if = "Option::is_none")] + pub foo: Option, +} + +impl MultigetGet201Response { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> MultigetGet201Response { + MultigetGet201Response { foo: None } + } +} + +/// Converts the MultigetGet201Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MultigetGet201Response { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .foo + .as_ref() + .map(|foo| ["foo".to_string(), foo.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MultigetGet201Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MultigetGet201Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub foo: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing MultigetGet201Response".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "foo" => intermediate_rep.foo.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing MultigetGet201Response".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MultigetGet201Response { + foo: intermediate_rep.foo.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for MultigetGet201Response - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into MultigetGet201Response - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MyId(i32); + +impl validator::Validate for MyId { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for MyId { + fn from(x: i32) -> Self { + MyId(x) + } +} + +impl std::convert::From for i32 { + fn from(x: MyId) -> Self { + x.0 + } +} + +impl std::ops::Deref for MyId { + type Target = i32; + fn deref(&self) -> &i32 { + &self.0 + } +} + +impl std::ops::DerefMut for MyId { + fn deref_mut(&mut self) -> &mut i32 { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MyIdList(Vec); + +impl validator::Validate for MyIdList { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for MyIdList { + fn from(x: Vec) -> Self { + MyIdList(x) + } +} + +impl std::convert::From for Vec { + fn from(x: MyIdList) -> Self { + x.0 + } +} + +impl std::iter::FromIterator for MyIdList { + fn from_iter>(u: U) -> Self { + MyIdList(Vec::::from_iter(u)) + } +} + +impl std::iter::IntoIterator for MyIdList { + type Item = i32; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a MyIdList { + type Item = &'a i32; + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a mut MyIdList { + type Item = &'a mut i32; + type IntoIter = std::slice::IterMut<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl std::ops::Deref for MyIdList { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for MyIdList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Converts the MyIdList value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MyIdList { + fn to_string(&self) -> String { + self.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MyIdList value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MyIdList { + type Err = ::Err; + + fn from_str(s: &str) -> std::result::Result { + let mut items = vec![]; + for item in s.split(',') { + items.push(item.parse()?); + } + std::result::Result::Ok(MyIdList(items)) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for MyIdList - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into MyIdList - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct NullableTest { + #[serde(rename = "nullable")] + pub nullable: Nullable, + + #[serde(rename = "nullableWithNullDefault")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_with_null_default: Option>, + + #[serde(rename = "nullableWithPresentDefault")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_with_present_default: Option>, + + #[serde(rename = "nullableWithNoDefault")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_with_no_default: Option>, + + #[serde(rename = "nullableArray")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_array: Option>>, + + #[serde(rename = "min_item_test")] + #[validate(length(min = 1))] + #[serde(skip_serializing_if = "Option::is_none")] + pub min_item_test: Option>, + + #[serde(rename = "max_item_test")] + #[validate(length(max = 2))] + #[serde(skip_serializing_if = "Option::is_none")] + pub max_item_test: Option>, + + #[serde(rename = "min_max_item_test")] + #[validate(length(min = 1, max = 3))] + #[serde(skip_serializing_if = "Option::is_none")] + pub min_max_item_test: Option>, +} + +impl NullableTest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(nullable: Nullable) -> NullableTest { + NullableTest { + nullable, + nullable_with_null_default: None, + nullable_with_present_default: Some(Nullable::Present("default".to_string())), + nullable_with_no_default: None, + nullable_array: None, + min_item_test: None, + max_item_test: None, + min_max_item_test: None, + } + } +} + +/// Converts the NullableTest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for NullableTest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("nullable".to_string()), + Some( + self.nullable + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ), + self.nullable_with_null_default + .as_ref() + .map(|nullable_with_null_default| { + [ + "nullableWithNullDefault".to_string(), + nullable_with_null_default + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ] + .join(",") + }), + self.nullable_with_present_default + .as_ref() + .map(|nullable_with_present_default| { + [ + "nullableWithPresentDefault".to_string(), + nullable_with_present_default + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ] + .join(",") + }), + self.nullable_with_no_default + .as_ref() + .map(|nullable_with_no_default| { + [ + "nullableWithNoDefault".to_string(), + nullable_with_no_default + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ] + .join(",") + }), + self.nullable_array.as_ref().map(|nullable_array| { + [ + "nullableArray".to_string(), + nullable_array.as_ref().map_or("null".to_string(), |x| { + x.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + }), + ] + .join(",") + }), + self.min_item_test.as_ref().map(|min_item_test| { + [ + "min_item_test".to_string(), + min_item_test + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + self.max_item_test.as_ref().map(|max_item_test| { + [ + "max_item_test".to_string(), + max_item_test + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + self.min_max_item_test.as_ref().map(|min_max_item_test| { + [ + "min_max_item_test".to_string(), + min_max_item_test + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a NullableTest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for NullableTest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub nullable: Vec, + pub nullable_with_null_default: Vec, + pub nullable_with_present_default: Vec, + pub nullable_with_no_default: Vec, + pub nullable_array: Vec>, + pub min_item_test: Vec>, + pub max_item_test: Vec>, + pub min_max_item_test: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing NullableTest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "nullable" => return std::result::Result::Err( + "Parsing a nullable type in this style is not supported in NullableTest" + .to_string(), + ), + "nullableWithNullDefault" => return std::result::Result::Err( + "Parsing a nullable type in this style is not supported in NullableTest" + .to_string(), + ), + "nullableWithPresentDefault" => return std::result::Result::Err( + "Parsing a nullable type in this style is not supported in NullableTest" + .to_string(), + ), + "nullableWithNoDefault" => return std::result::Result::Err( + "Parsing a nullable type in this style is not supported in NullableTest" + .to_string(), + ), + "nullableArray" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in NullableTest" + .to_string(), + ) + } + "min_item_test" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in NullableTest" + .to_string(), + ) + } + "max_item_test" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in NullableTest" + .to_string(), + ) + } + "min_max_item_test" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in NullableTest" + .to_string(), + ) + } + _ => { + return std::result::Result::Err( + "Unexpected key while parsing NullableTest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(NullableTest { + nullable: std::result::Result::Err( + "Nullable types not supported in NullableTest".to_string(), + )?, + nullable_with_null_default: std::result::Result::Err( + "Nullable types not supported in NullableTest".to_string(), + )?, + nullable_with_present_default: std::result::Result::Err( + "Nullable types not supported in NullableTest".to_string(), + )?, + nullable_with_no_default: std::result::Result::Err( + "Nullable types not supported in NullableTest".to_string(), + )?, + nullable_array: std::result::Result::Err( + "Nullable types not supported in NullableTest".to_string(), + )?, + min_item_test: intermediate_rep.min_item_test.into_iter().next(), + max_item_test: intermediate_rep.max_item_test.into_iter().next(), + min_max_item_test: intermediate_rep.min_max_item_test.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for NullableTest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into NullableTest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectHeader { + #[serde(rename = "requiredObjectHeader")] + pub required_object_header: bool, + + #[serde(rename = "optionalObjectHeader")] + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_object_header: Option, +} + +impl ObjectHeader { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_object_header: bool) -> ObjectHeader { + ObjectHeader { + required_object_header, + optional_object_header: None, + } + } +} + +/// Converts the ObjectHeader value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectHeader { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("requiredObjectHeader".to_string()), + Some(self.required_object_header.to_string()), + self.optional_object_header + .as_ref() + .map(|optional_object_header| { + [ + "optionalObjectHeader".to_string(), + optional_object_header.to_string(), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectHeader value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectHeader { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub required_object_header: Vec, + pub optional_object_header: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectHeader".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "requiredObjectHeader" => intermediate_rep.required_object_header.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "optionalObjectHeader" => intermediate_rep.optional_object_header.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ObjectHeader".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectHeader { + required_object_header: intermediate_rep + .required_object_header + .into_iter() + .next() + .ok_or_else(|| "requiredObjectHeader missing in ObjectHeader".to_string())?, + optional_object_header: intermediate_rep.optional_object_header.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectHeader - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectHeader - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectParam { + #[serde(rename = "requiredParam")] + pub required_param: bool, + + #[serde(rename = "optionalParam")] + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_param: Option, +} + +impl ObjectParam { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_param: bool) -> ObjectParam { + ObjectParam { + required_param, + optional_param: None, + } + } +} + +/// Converts the ObjectParam value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectParam { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("requiredParam".to_string()), + Some(self.required_param.to_string()), + self.optional_param.as_ref().map(|optional_param| { + ["optionalParam".to_string(), optional_param.to_string()].join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectParam value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectParam { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub required_param: Vec, + pub optional_param: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectParam".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "requiredParam" => intermediate_rep.required_param.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "optionalParam" => intermediate_rep.optional_param.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ObjectParam".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectParam { + required_param: intermediate_rep + .required_param + .into_iter() + .next() + .ok_or_else(|| "requiredParam missing in ObjectParam".to_string())?, + optional_param: intermediate_rep.optional_param.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectParam - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectParam - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectUntypedProps { + #[serde(rename = "required_untyped")] + pub required_untyped: crate::types::Object, + + #[serde(rename = "required_untyped_nullable")] + pub required_untyped_nullable: Nullable, + + #[serde(rename = "not_required_untyped")] + #[serde(skip_serializing_if = "Option::is_none")] + pub not_required_untyped: Option, + + #[serde(rename = "not_required_untyped_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub not_required_untyped_nullable: Option, +} + +impl ObjectUntypedProps { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new( + required_untyped: crate::types::Object, + required_untyped_nullable: Nullable, + ) -> ObjectUntypedProps { + ObjectUntypedProps { + required_untyped, + required_untyped_nullable, + not_required_untyped: None, + not_required_untyped_nullable: None, + } + } +} + +/// Converts the ObjectUntypedProps value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectUntypedProps { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping required_untyped in query parameter serialization + + // Skipping required_untyped_nullable in query parameter serialization + + // Skipping not_required_untyped in query parameter serialization + + // Skipping not_required_untyped_nullable in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectUntypedProps value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectUntypedProps { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub required_untyped: Vec, + pub required_untyped_nullable: Vec, + pub not_required_untyped: Vec, + pub not_required_untyped_nullable: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectUntypedProps".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "required_untyped" => intermediate_rep.required_untyped.push(::from_str(val).map_err(|x| x.to_string())?), + "required_untyped_nullable" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in ObjectUntypedProps".to_string()), + #[allow(clippy::redundant_clone)] + "not_required_untyped" => intermediate_rep.not_required_untyped.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "not_required_untyped_nullable" => intermediate_rep.not_required_untyped_nullable.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing ObjectUntypedProps".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectUntypedProps { + required_untyped: intermediate_rep + .required_untyped + .into_iter() + .next() + .ok_or_else(|| "required_untyped missing in ObjectUntypedProps".to_string())?, + required_untyped_nullable: std::result::Result::Err( + "Nullable types not supported in ObjectUntypedProps".to_string(), + )?, + not_required_untyped: intermediate_rep.not_required_untyped.into_iter().next(), + not_required_untyped_nullable: intermediate_rep + .not_required_untyped_nullable + .into_iter() + .next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectUntypedProps - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectUntypedProps - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectWithArrayOfObjects { + #[serde(rename = "objectArray")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_array: Option>, +} + +impl ObjectWithArrayOfObjects { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ObjectWithArrayOfObjects { + ObjectWithArrayOfObjects { object_array: None } + } +} + +/// Converts the ObjectWithArrayOfObjects value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectWithArrayOfObjects { + fn to_string(&self) -> String { + let params: Vec> = vec![self.object_array.as_ref().map(|object_array| { + [ + "objectArray".to_string(), + object_array + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + })]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectWithArrayOfObjects value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectWithArrayOfObjects { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub object_array: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectWithArrayOfObjects".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "objectArray" => return std::result::Result::Err("Parsing a container in this style is not supported in ObjectWithArrayOfObjects".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing ObjectWithArrayOfObjects".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectWithArrayOfObjects { + object_array: intermediate_rep.object_array.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectWithArrayOfObjects - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectWithArrayOfObjects - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Ok(String); + +impl validator::Validate for Ok { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for Ok { + fn from(x: String) -> Self { + Ok(x) + } +} + +impl std::string::ToString for Ok { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for Ok { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(Ok(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: Ok) -> Self { + x.0 + } +} + +impl std::ops::Deref for Ok { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for Ok { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// One of: +/// - Vec +/// - i32 +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct OneOfGet200Response(Box); + +impl validator::Validate for OneOfGet200Response { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a OneOfGet200Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for OneOfGet200Response { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for OneOfGet200Response { + fn eq(&self, other: &Self) -> bool { + self.0.get().eq(other.0.get()) + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct OptionalObjectHeader(i32); + +impl validator::Validate for OptionalObjectHeader { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for OptionalObjectHeader { + fn from(x: i32) -> Self { + OptionalObjectHeader(x) + } +} + +impl std::convert::From for i32 { + fn from(x: OptionalObjectHeader) -> Self { + x.0 + } +} + +impl std::ops::Deref for OptionalObjectHeader { + type Target = i32; + fn deref(&self) -> &i32 { + &self.0 + } +} + +impl std::ops::DerefMut for OptionalObjectHeader { + fn deref_mut(&mut self) -> &mut i32 { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RequiredObjectHeader(bool); + +impl validator::Validate for RequiredObjectHeader { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for RequiredObjectHeader { + fn from(x: bool) -> Self { + RequiredObjectHeader(x) + } +} + +impl std::convert::From for bool { + fn from(x: RequiredObjectHeader) -> Self { + x.0 + } +} + +impl std::ops::Deref for RequiredObjectHeader { + type Target = bool; + fn deref(&self) -> &bool { + &self.0 + } +} + +impl std::ops::DerefMut for RequiredObjectHeader { + fn deref_mut(&mut self) -> &mut bool { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Result(String); + +impl validator::Validate for Result { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for Result { + fn from(x: String) -> Self { + Result(x) + } +} + +impl std::string::ToString for Result { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for Result { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(Result(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: Result) -> Self { + x.0 + } +} + +impl std::ops::Deref for Result { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for Result { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))] +pub enum StringEnum { + #[serde(rename = "FOO")] + Foo, + #[serde(rename = "BAR")] + Bar, +} + +impl std::fmt::Display for StringEnum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + StringEnum::Foo => write!(f, "FOO"), + StringEnum::Bar => write!(f, "BAR"), + } + } +} + +impl std::str::FromStr for StringEnum { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + "FOO" => std::result::Result::Ok(StringEnum::Foo), + "BAR" => std::result::Result::Ok(StringEnum::Bar), + _ => std::result::Result::Err(format!("Value not valid: {}", s)), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct StringObject(String); + +impl validator::Validate for StringObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for StringObject { + fn from(x: String) -> Self { + StringObject(x) + } +} + +impl std::string::ToString for StringObject { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for StringObject { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(StringObject(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: StringObject) -> Self { + x.0 + } +} + +impl std::ops::Deref for StringObject { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for StringObject { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// Test a model containing a UUID +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UuidObject(uuid::Uuid); + +impl validator::Validate for UuidObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for UuidObject { + fn from(x: uuid::Uuid) -> Self { + UuidObject(x) + } +} + +impl std::convert::From for uuid::Uuid { + fn from(x: UuidObject) -> Self { + x.0 + } +} + +impl std::ops::Deref for UuidObject { + type Target = uuid::Uuid; + fn deref(&self) -> &uuid::Uuid { + &self.0 + } +} + +impl std::ops::DerefMut for UuidObject { + fn deref_mut(&mut self) -> &mut uuid::Uuid { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct XmlArray(Vec); + +impl validator::Validate for XmlArray { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for XmlArray { + fn from(x: Vec) -> Self { + XmlArray(x) + } +} + +impl std::convert::From for Vec { + fn from(x: XmlArray) -> Self { + x.0 + } +} + +impl std::iter::FromIterator for XmlArray { + fn from_iter>(u: U) -> Self { + XmlArray(Vec::::from_iter(u)) + } +} + +impl std::iter::IntoIterator for XmlArray { + type Item = String; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a XmlArray { + type Item = &'a String; + type IntoIter = std::slice::Iter<'a, String>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a mut XmlArray { + type Item = &'a mut String; + type IntoIter = std::slice::IterMut<'a, String>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl std::ops::Deref for XmlArray { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for XmlArray { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Converts the XmlArray value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for XmlArray { + fn to_string(&self) -> String { + self.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a XmlArray value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for XmlArray { + type Err = ::Err; + + fn from_str(s: &str) -> std::result::Result { + let mut items = vec![]; + for item in s.split(',') { + items.push(item.parse()?); + } + std::result::Result::Ok(XmlArray(items)) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for XmlArray - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into XmlArray - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct XmlInner(String); + +impl validator::Validate for XmlInner { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for XmlInner { + fn from(x: String) -> Self { + XmlInner(x) + } +} + +impl std::string::ToString for XmlInner { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for XmlInner { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(XmlInner(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: XmlInner) -> Self { + x.0 + } +} + +impl std::ops::Deref for XmlInner { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for XmlInner { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +/// An XML object + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct XmlObject { + #[serde(rename = "innerString")] + #[serde(skip_serializing_if = "Option::is_none")] + pub inner_string: Option, + + #[serde(rename = "other_inner_rename")] + #[serde(skip_serializing_if = "Option::is_none")] + pub other_inner_rename: Option, +} + +impl XmlObject { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> XmlObject { + XmlObject { + inner_string: None, + other_inner_rename: None, + } + } +} + +/// Converts the XmlObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for XmlObject { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.inner_string.as_ref().map(|inner_string| { + ["innerString".to_string(), inner_string.to_string()].join(",") + }), + self.other_inner_rename.as_ref().map(|other_inner_rename| { + [ + "other_inner_rename".to_string(), + other_inner_rename.to_string(), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a XmlObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for XmlObject { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inner_string: Vec, + pub other_inner_rename: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing XmlObject".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "innerString" => intermediate_rep.inner_string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "other_inner_rename" => intermediate_rep.other_inner_rename.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing XmlObject".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(XmlObject { + inner_string: intermediate_rep.inner_string.into_iter().next(), + other_inner_rename: intermediate_rep.other_inner_rename.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for XmlObject - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into XmlObject - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/server/mod.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/server/mod.rs new file mode 100644 index 00000000000..bd9213bb744 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/server/mod.rs @@ -0,0 +1,2098 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{ + AnyOfGetResponse, Api, CallbackWithHeaderPostResponse, ComplexQueryParamGetResponse, + CreateRepoResponse, EnumInPathPathParamGetResponse, GetRepoInfoResponse, + JsonComplexQueryParamGetResponse, MandatoryRequestHeaderGetResponse, MergePatchJsonGetResponse, + MultigetGetResponse, MultipleAuthSchemeGetResponse, OneOfGetResponse, + OverrideServerGetResponse, ParamgetGetResponse, ReadonlyAuthSchemeGetResponse, + RegisterCallbackPostResponse, RequiredOctetStreamPutResponse, ResponsesWithHeadersGetResponse, + Rfc7807GetResponse, UntypedPropertyGetResponse, UuidGetResponse, XmlExtraPostResponse, + XmlOtherPostResponse, XmlOtherPutResponse, XmlPostResponse, XmlPutResponse, +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/any-of", get(any_of_get::)) + .route( + "/callback-with-header", + post(callback_with_header_post::), + ) + .route("/complex-query-param", get(complex_query_param_get::)) + .route( + "/enum_in_path/:path_param", + get(enum_in_path_path_param_get::), + ) + .route( + "/json-complex-query-param", + get(json_complex_query_param_get::), + ) + .route( + "/mandatory-request-header", + get(mandatory_request_header_get::), + ) + .route("/merge-patch-json", get(merge_patch_json_get::)) + .route("/multiget", get(multiget_get::)) + .route( + "/multiple_auth_scheme", + get(multiple_auth_scheme_get::), + ) + .route("/one-of", get(one_of_get::)) + .route("/override-server", get(override_server_get::)) + .route("/paramget", get(paramget_get::)) + .route( + "/readonly_auth_scheme", + get(readonly_auth_scheme_get::), + ) + .route("/register-callback", post(register_callback_post::)) + .route("/repos", post(create_repo::)) + .route( + "/repos/:repo_id", + get(get_repo_info::).get(get_repo_info::), + ) + .route( + "/required_octet_stream", + put(required_octet_stream_put::), + ) + .route( + "/responses_with_headers", + get(responses_with_headers_get::), + ) + .route("/rfc7807", get(rfc7807_get::)) + .route("/untyped_property", get(untyped_property_get::)) + .route("/uuid", get(uuid_get::)) + .route("/xml", post(xml_post::).put(xml_put::)) + .route("/xml_extra", post(xml_extra_post::)) + .route( + "/xml_other", + post(xml_other_post::).put(xml_other_put::), + ) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn any_of_get_validation( + query_params: models::AnyOfGetQueryParams, +) -> std::result::Result<(models::AnyOfGetQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// AnyOfGet - GET /any-of +#[tracing::instrument(skip_all)] +async fn any_of_get( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = any_of_get_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .any_of_get(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + AnyOfGetResponse::Success(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + AnyOfGetResponse::AlternateSuccess(body) => { + let mut response = response.status(201); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + AnyOfGetResponse::AnyOfSuccess(body) => { + let mut response = response.status(202); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn callback_with_header_post_validation( + query_params: models::CallbackWithHeaderPostQueryParams, +) -> std::result::Result<(models::CallbackWithHeaderPostQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// CallbackWithHeaderPost - POST /callback-with-header +#[tracing::instrument(skip_all)] +async fn callback_with_header_post( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = callback_with_header_post_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .callback_with_header_post(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CallbackWithHeaderPostResponse::OK => { + let mut response = response.status(204); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn complex_query_param_get_validation( + query_params: models::ComplexQueryParamGetQueryParams, +) -> std::result::Result<(models::ComplexQueryParamGetQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// ComplexQueryParamGet - GET /complex-query-param +#[tracing::instrument(skip_all)] +async fn complex_query_param_get( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = complex_query_param_get_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .complex_query_param_get(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + ComplexQueryParamGetResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn enum_in_path_path_param_get_validation( + path_params: models::EnumInPathPathParamGetPathParams, +) -> std::result::Result<(models::EnumInPathPathParamGetPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// EnumInPathPathParamGet - GET /enum_in_path/{path_param} +#[tracing::instrument(skip_all)] +async fn enum_in_path_path_param_get( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = enum_in_path_path_param_get_validation(path_params); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .enum_in_path_path_param_get(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + EnumInPathPathParamGetResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn json_complex_query_param_get_validation( + query_params: models::JsonComplexQueryParamGetQueryParams, +) -> std::result::Result<(models::JsonComplexQueryParamGetQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// JsonComplexQueryParamGet - GET /json-complex-query-param +#[tracing::instrument(skip_all)] +async fn json_complex_query_param_get( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = json_complex_query_param_get_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .json_complex_query_param_get(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + JsonComplexQueryParamGetResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn mandatory_request_header_get_validation( + header_params: models::MandatoryRequestHeaderGetHeaderParams, +) -> std::result::Result<(models::MandatoryRequestHeaderGetHeaderParams,), ValidationErrors> { + header_params.validate()?; + + Ok((header_params,)) +} + +/// MandatoryRequestHeaderGet - GET /mandatory-request-header +#[tracing::instrument(skip_all)] +async fn mandatory_request_header_get( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + // Header parameters + let header_params = { + let header_x_header = headers.get(HeaderName::from_static("x-header")); + + let header_x_header = match header_x_header { + Some(v) => match header::IntoHeaderValue::::try_from((*v).clone()) { + Ok(result) => result.0, + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Invalid header X-Header - {}", err))) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }, + None => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from("Missing required header X-Header")) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }; + + models::MandatoryRequestHeaderGetHeaderParams { + x_header: header_x_header, + } + }; + + let validation = mandatory_request_header_get_validation(header_params); + + let Ok((header_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .mandatory_request_header_get(method, host, cookies, header_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MandatoryRequestHeaderGetResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn merge_patch_json_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MergePatchJsonGet - GET /merge-patch-json +#[tracing::instrument(skip_all)] +async fn merge_patch_json_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = merge_patch_json_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .merge_patch_json_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MergePatchJsonGetResponse::Merge(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/merge-patch+json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn multiget_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MultigetGet - GET /multiget +#[tracing::instrument(skip_all)] +async fn multiget_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = multiget_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().multiget_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MultigetGetResponse::JSONRsp(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + MultigetGetResponse::XMLRsp(body) => { + let mut response = response.status(201); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + MultigetGetResponse::OctetRsp(body) => { + let mut response = response.status(202); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/octet-stream").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body.0; + response.body(Body::from(body_content)) + } + MultigetGetResponse::StringRsp(body) => { + let mut response = response.status(203); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + MultigetGetResponse::DuplicateResponseLongText(body) => { + let mut response = response.status(204); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + MultigetGetResponse::DuplicateResponseLongText_2(body) => { + let mut response = response.status(205); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + MultigetGetResponse::DuplicateResponseLongText_3(body) => { + let mut response = response.status(206); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn multiple_auth_scheme_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// MultipleAuthSchemeGet - GET /multiple_auth_scheme +#[tracing::instrument(skip_all)] +async fn multiple_auth_scheme_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = multiple_auth_scheme_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .multiple_auth_scheme_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + MultipleAuthSchemeGetResponse::CheckThatLimitingToMultipleRequiredAuthSchemesWorks => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn one_of_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// OneOfGet - GET /one-of +#[tracing::instrument(skip_all)] +async fn one_of_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = one_of_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().one_of_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + OneOfGetResponse::Success(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn override_server_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// OverrideServerGet - GET /override-server +#[tracing::instrument(skip_all)] +async fn override_server_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = override_server_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .override_server_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + OverrideServerGetResponse::Success => { + let mut response = response.status(204); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn paramget_get_validation( + query_params: models::ParamgetGetQueryParams, +) -> std::result::Result<(models::ParamgetGetQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// ParamgetGet - GET /paramget +#[tracing::instrument(skip_all)] +async fn paramget_get( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = paramget_get_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .paramget_get(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + ParamgetGetResponse::JSONRsp(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn readonly_auth_scheme_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// ReadonlyAuthSchemeGet - GET /readonly_auth_scheme +#[tracing::instrument(skip_all)] +async fn readonly_auth_scheme_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = readonly_auth_scheme_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .readonly_auth_scheme_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + ReadonlyAuthSchemeGetResponse::CheckThatLimitingToASingleRequiredAuthSchemeWorks => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn register_callback_post_validation( + query_params: models::RegisterCallbackPostQueryParams, +) -> std::result::Result<(models::RegisterCallbackPostQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// RegisterCallbackPost - POST /register-callback +#[tracing::instrument(skip_all)] +async fn register_callback_post( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = register_callback_post_validation(query_params); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .register_callback_post(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + RegisterCallbackPostResponse::OK => { + let mut response = response.status(204); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct RequiredOctetStreamPutBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn required_octet_stream_put_validation( + body: Bytes, +) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// RequiredOctetStreamPut - PUT /required_octet_stream +#[tracing::instrument(skip_all)] +async fn required_octet_stream_put( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = required_octet_stream_put_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .required_octet_stream_put(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + RequiredOctetStreamPutResponse::OK => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn responses_with_headers_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// ResponsesWithHeadersGet - GET /responses_with_headers +#[tracing::instrument(skip_all)] +async fn responses_with_headers_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = responses_with_headers_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .responses_with_headers_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + ResponsesWithHeadersGetResponse::Success { + body, + success_info, + bool_header, + object_header, + } => { + let success_info = match header::IntoHeaderValue(success_info).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling success_info header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert(HeaderName::from_static("success-info"), success_info); + } + if let Some(bool_header) = bool_header { + let bool_header = match header::IntoHeaderValue(bool_header).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling bool_header header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("bool-header"), bool_header); + } + } + if let Some(object_header) = object_header { + let object_header = match header::IntoHeaderValue(object_header).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling object_header header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("object-header"), object_header); + } + } + + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + ResponsesWithHeadersGetResponse::PreconditionFailed { + further_info, + failure_info, + } => { + if let Some(further_info) = further_info { + let further_info = match header::IntoHeaderValue(further_info).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling further_info header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("further-info"), further_info); + } + } + if let Some(failure_info) = failure_info { + let failure_info = match header::IntoHeaderValue(failure_info).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling failure_info header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("failure-info"), failure_info); + } + } + + let mut response = response.status(412); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn rfc7807_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// Rfc7807Get - GET /rfc7807 +#[tracing::instrument(skip_all)] +async fn rfc7807_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = rfc7807_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().rfc7807_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Rfc7807GetResponse::OK(body) => { + let mut response = response.status(204); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + Rfc7807GetResponse::NotFound(body) => { + let mut response = response.status(404); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/problem+json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + Rfc7807GetResponse::NotAcceptable(body) => { + let mut response = response.status(406); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct UntypedPropertyGetBodyValidator<'a> { + #[validate] + body: &'a models::ObjectUntypedProps, +} + +#[tracing::instrument(skip_all)] +fn untyped_property_get_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = UntypedPropertyGetBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} + +/// UntypedPropertyGet - GET /untyped_property +#[tracing::instrument(skip_all)] +async fn untyped_property_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = untyped_property_get_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .untyped_property_get(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UntypedPropertyGetResponse::CheckThatUntypedPropertiesWorks => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn uuid_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// UuidGet - GET /uuid +#[tracing::instrument(skip_all)] +async fn uuid_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = uuid_get_validation(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().uuid_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UuidGetResponse::DuplicateResponseLongText(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct XmlExtraPostBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn xml_extra_post_validation(body: Bytes) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// XmlExtraPost - POST /xml_extra +#[tracing::instrument(skip_all)] +async fn xml_extra_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = xml_extra_post_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .xml_extra_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + XmlExtraPostResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + XmlExtraPostResponse::BadRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct XmlOtherPostBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn xml_other_post_validation(body: Bytes) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// XmlOtherPost - POST /xml_other +#[tracing::instrument(skip_all)] +async fn xml_other_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = xml_other_post_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .xml_other_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + XmlOtherPostResponse::OK(body) => { + let mut response = response.status(201); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + XmlOtherPostResponse::BadRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct XmlOtherPutBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn xml_other_put_validation(body: Bytes) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// XmlOtherPut - PUT /xml_other +#[tracing::instrument(skip_all)] +async fn xml_other_put( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = xml_other_put_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .xml_other_put(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + XmlOtherPutResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + XmlOtherPutResponse::BadRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct XmlPostBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn xml_post_validation(body: Bytes) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// XmlPost - POST /xml +#[tracing::instrument(skip_all)] +async fn xml_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = xml_post_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .xml_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + XmlPostResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + XmlPostResponse::BadRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct XmlPutBodyValidator<'a> { + body: &'a [u8], +} + +#[tracing::instrument(skip_all)] +fn xml_put_validation(body: Bytes) -> std::result::Result<(Bytes,), ValidationErrors> { + Ok((body,)) +} + +/// XmlPut - PUT /xml +#[tracing::instrument(skip_all)] +async fn xml_put( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: Bytes, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = xml_put_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().xml_put(method, host, cookies, body).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + XmlPutResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + XmlPutResponse::BadRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateRepoBodyValidator<'a> { + #[validate] + body: &'a models::ObjectParam, +} + +#[tracing::instrument(skip_all)] +fn create_repo_validation( + body: models::ObjectParam, +) -> std::result::Result<(models::ObjectParam,), ValidationErrors> { + let b = CreateRepoBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateRepo - POST /repos +#[tracing::instrument(skip_all)] +async fn create_repo( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = create_repo_validation(body); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_repo(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateRepoResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_repo_info_validation( + path_params: models::GetRepoInfoPathParams, +) -> std::result::Result<(models::GetRepoInfoPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetRepoInfo - GET /repos/{repoId} +#[tracing::instrument(skip_all)] +async fn get_repo_info( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + let validation = get_repo_info_validation(path_params); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_repo_info(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetRepoInfoResponse::OK(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs new file mode 100644 index 00000000000..1c59471ea54 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use openapi_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use openapi_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use openapi_v3::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/ops-v3/.gitignore b/samples/server/petstore/rust-axum/output/ops-v3/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml new file mode 100644 index 00000000000..80a1405875e --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "ops-v3" +version = "0.0.1" +authors = ["OpenAPI Generator team and contributors"] +description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/ops-v3/README.md b/samples/server/petstore/rust-axum/output/ops-v3/README.md new file mode 100644 index 00000000000..c32fe05320a --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/README.md @@ -0,0 +1,91 @@ +# Rust API for ops-v3 + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 0.0.1 + + + + +This autogenerated project defines an API crate `ops-v3` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl ops-v3::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = ops-v3::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/header.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/header.rs new file mode 100644 index 00000000000..4d1cc4c6dcc --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/header.rs @@ -0,0 +1,180 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse {} as a string: {}", + stringify!($t), e)), + }, + Err(e) => Err(format!("Unable to parse header {:?} as a string - {}", + hdr_value, e)), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect())), + Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} into a header - {}", + hdr_value, e)) + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)) + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert: {:?} into a header: {}", + hdr_value, e)) + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert header {:?} to string {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} to a header: {}", + hdr_value, e)), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/lib.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/lib.rs new file mode 100644 index 00000000000..93e8d9f800c --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/lib.rs @@ -0,0 +1,586 @@ +#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "0.0.1"; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op10GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op11GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op12GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op13GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op14GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op15GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op16GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op17GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op18GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op19GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op1GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op20GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op21GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op22GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op23GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op24GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op25GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op26GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op27GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op28GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op29GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op2GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op30GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op31GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op32GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op33GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op34GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op35GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op36GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op37GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op3GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op4GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op5GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op6GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op7GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op8GetResponse { + /// OK + OK +} + + #[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Op9GetResponse { + /// OK + OK +} + + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + + /// Op10Get - GET /op10 + async fn op10_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op11Get - GET /op11 + async fn op11_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op12Get - GET /op12 + async fn op12_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op13Get - GET /op13 + async fn op13_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op14Get - GET /op14 + async fn op14_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op15Get - GET /op15 + async fn op15_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op16Get - GET /op16 + async fn op16_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op17Get - GET /op17 + async fn op17_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op18Get - GET /op18 + async fn op18_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op19Get - GET /op19 + async fn op19_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op1Get - GET /op1 + async fn op1_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op20Get - GET /op20 + async fn op20_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op21Get - GET /op21 + async fn op21_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op22Get - GET /op22 + async fn op22_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op23Get - GET /op23 + async fn op23_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op24Get - GET /op24 + async fn op24_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op25Get - GET /op25 + async fn op25_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op26Get - GET /op26 + async fn op26_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op27Get - GET /op27 + async fn op27_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op28Get - GET /op28 + async fn op28_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op29Get - GET /op29 + async fn op29_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op2Get - GET /op2 + async fn op2_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op30Get - GET /op30 + async fn op30_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op31Get - GET /op31 + async fn op31_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op32Get - GET /op32 + async fn op32_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op33Get - GET /op33 + async fn op33_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op34Get - GET /op34 + async fn op34_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op35Get - GET /op35 + async fn op35_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op36Get - GET /op36 + async fn op36_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op37Get - GET /op37 + async fn op37_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op3Get - GET /op3 + async fn op3_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op4Get - GET /op4 + async fn op4_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op5Get - GET /op5 + async fn op5_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op6Get - GET /op6 + async fn op6_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op7Get - GET /op7 + async fn op7_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op8Get - GET /op8 + async fn op8_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + + /// Op9Get - GET /op9 + async fn op9_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/models.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/models.rs new file mode 100644 index 00000000000..3ba24da3c35 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/models.rs @@ -0,0 +1,47 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/server/mod.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/server/mod.rs new file mode 100644 index 00000000000..3173578f27c --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/server/mod.rs @@ -0,0 +1,2581 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{Api, + Op10GetResponse, + Op11GetResponse, + Op12GetResponse, + Op13GetResponse, + Op14GetResponse, + Op15GetResponse, + Op16GetResponse, + Op17GetResponse, + Op18GetResponse, + Op19GetResponse, + Op1GetResponse, + Op20GetResponse, + Op21GetResponse, + Op22GetResponse, + Op23GetResponse, + Op24GetResponse, + Op25GetResponse, + Op26GetResponse, + Op27GetResponse, + Op28GetResponse, + Op29GetResponse, + Op2GetResponse, + Op30GetResponse, + Op31GetResponse, + Op32GetResponse, + Op33GetResponse, + Op34GetResponse, + Op35GetResponse, + Op36GetResponse, + Op37GetResponse, + Op3GetResponse, + Op4GetResponse, + Op5GetResponse, + Op6GetResponse, + Op7GetResponse, + Op8GetResponse, + Op9GetResponse +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/op1", + get(op1_get::) + ) + .route("/op10", + get(op10_get::) + ) + .route("/op11", + get(op11_get::) + ) + .route("/op12", + get(op12_get::) + ) + .route("/op13", + get(op13_get::) + ) + .route("/op14", + get(op14_get::) + ) + .route("/op15", + get(op15_get::) + ) + .route("/op16", + get(op16_get::) + ) + .route("/op17", + get(op17_get::) + ) + .route("/op18", + get(op18_get::) + ) + .route("/op19", + get(op19_get::) + ) + .route("/op2", + get(op2_get::) + ) + .route("/op20", + get(op20_get::) + ) + .route("/op21", + get(op21_get::) + ) + .route("/op22", + get(op22_get::) + ) + .route("/op23", + get(op23_get::) + ) + .route("/op24", + get(op24_get::) + ) + .route("/op25", + get(op25_get::) + ) + .route("/op26", + get(op26_get::) + ) + .route("/op27", + get(op27_get::) + ) + .route("/op28", + get(op28_get::) + ) + .route("/op29", + get(op29_get::) + ) + .route("/op3", + get(op3_get::) + ) + .route("/op30", + get(op30_get::) + ) + .route("/op31", + get(op31_get::) + ) + .route("/op32", + get(op32_get::) + ) + .route("/op33", + get(op33_get::) + ) + .route("/op34", + get(op34_get::) + ) + .route("/op35", + get(op35_get::) + ) + .route("/op36", + get(op36_get::) + ) + .route("/op37", + get(op37_get::) + ) + .route("/op4", + get(op4_get::) + ) + .route("/op5", + get(op5_get::) + ) + .route("/op6", + get(op6_get::) + ) + .route("/op7", + get(op7_get::) + ) + .route("/op8", + get(op8_get::) + ) + .route("/op9", + get(op9_get::) + ) + .with_state(api_impl) +} + + +#[tracing::instrument(skip_all)] +fn op10_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op10Get - GET /op10 +#[tracing::instrument(skip_all)] +async fn op10_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op10_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op10_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op10GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op11_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op11Get - GET /op11 +#[tracing::instrument(skip_all)] +async fn op11_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op11_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op11_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op11GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op12_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op12Get - GET /op12 +#[tracing::instrument(skip_all)] +async fn op12_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op12_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op12_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op12GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op13_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op13Get - GET /op13 +#[tracing::instrument(skip_all)] +async fn op13_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op13_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op13_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op13GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op14_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op14Get - GET /op14 +#[tracing::instrument(skip_all)] +async fn op14_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op14_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op14_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op14GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op15_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op15Get - GET /op15 +#[tracing::instrument(skip_all)] +async fn op15_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op15_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op15_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op15GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op16_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op16Get - GET /op16 +#[tracing::instrument(skip_all)] +async fn op16_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op16_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op16_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op16GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op17_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op17Get - GET /op17 +#[tracing::instrument(skip_all)] +async fn op17_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op17_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op17_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op17GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op18_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op18Get - GET /op18 +#[tracing::instrument(skip_all)] +async fn op18_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op18_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op18_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op18GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op19_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op19Get - GET /op19 +#[tracing::instrument(skip_all)] +async fn op19_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op19_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op19_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op19GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op1_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op1Get - GET /op1 +#[tracing::instrument(skip_all)] +async fn op1_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op1_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op1_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op1GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op20_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op20Get - GET /op20 +#[tracing::instrument(skip_all)] +async fn op20_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op20_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op20_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op20GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op21_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op21Get - GET /op21 +#[tracing::instrument(skip_all)] +async fn op21_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op21_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op21_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op21GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op22_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op22Get - GET /op22 +#[tracing::instrument(skip_all)] +async fn op22_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op22_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op22_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op22GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op23_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op23Get - GET /op23 +#[tracing::instrument(skip_all)] +async fn op23_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op23_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op23_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op23GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op24_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op24Get - GET /op24 +#[tracing::instrument(skip_all)] +async fn op24_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op24_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op24_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op24GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op25_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op25Get - GET /op25 +#[tracing::instrument(skip_all)] +async fn op25_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op25_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op25_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op25GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op26_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op26Get - GET /op26 +#[tracing::instrument(skip_all)] +async fn op26_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op26_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op26_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op26GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op27_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op27Get - GET /op27 +#[tracing::instrument(skip_all)] +async fn op27_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op27_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op27_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op27GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op28_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op28Get - GET /op28 +#[tracing::instrument(skip_all)] +async fn op28_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op28_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op28_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op28GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op29_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op29Get - GET /op29 +#[tracing::instrument(skip_all)] +async fn op29_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op29_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op29_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op29GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op2_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op2Get - GET /op2 +#[tracing::instrument(skip_all)] +async fn op2_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op2_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op2_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op2GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op30_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op30Get - GET /op30 +#[tracing::instrument(skip_all)] +async fn op30_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op30_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op30_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op30GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op31_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op31Get - GET /op31 +#[tracing::instrument(skip_all)] +async fn op31_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op31_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op31_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op31GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op32_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op32Get - GET /op32 +#[tracing::instrument(skip_all)] +async fn op32_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op32_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op32_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op32GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op33_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op33Get - GET /op33 +#[tracing::instrument(skip_all)] +async fn op33_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op33_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op33_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op33GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op34_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op34Get - GET /op34 +#[tracing::instrument(skip_all)] +async fn op34_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op34_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op34_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op34GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op35_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op35Get - GET /op35 +#[tracing::instrument(skip_all)] +async fn op35_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op35_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op35_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op35GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op36_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op36Get - GET /op36 +#[tracing::instrument(skip_all)] +async fn op36_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op36_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op36_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op36GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op37_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op37Get - GET /op37 +#[tracing::instrument(skip_all)] +async fn op37_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op37_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op37_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op37GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op3_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op3Get - GET /op3 +#[tracing::instrument(skip_all)] +async fn op3_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op3_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op3_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op3GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op4_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op4Get - GET /op4 +#[tracing::instrument(skip_all)] +async fn op4_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op4_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op4_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op4GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op5_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op5Get - GET /op5 +#[tracing::instrument(skip_all)] +async fn op5_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op5_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op5_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op5GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op6_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op6Get - GET /op6 +#[tracing::instrument(skip_all)] +async fn op6_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op6_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op6_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op6GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op7_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op7Get - GET /op7 +#[tracing::instrument(skip_all)] +async fn op7_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op7_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op7_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op7GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op8_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op8Get - GET /op8 +#[tracing::instrument(skip_all)] +async fn op8_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op8_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op8_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op8GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn op9_get_validation( +) -> std::result::Result<( +), ValidationErrors> +{ + +Ok(( +)) +} + +/// Op9Get - GET /op9 +#[tracing::instrument(skip_all)] +async fn op9_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + op9_get_validation( + ) + ).await.unwrap(); + + let Ok(( + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().op9_get( + method, + host, + cookies, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Op9GetResponse::OK + => { + + let mut response = response.status(200); + response.body(Body::empty()) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/types.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/types.rs new file mode 100644 index 00000000000..98c3d7a00cd --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use ops_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use ops_v3::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use ops_v3::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.gitignore b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml new file mode 100644 index 00000000000..42f579e8e99 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "petstore-with-fake-endpoints-models-for-testing" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\" +license = "Apache-2.0" +edition = "2021" +publish = ["crates-io"] + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/README.md b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/README.md new file mode 100644 index 00000000000..66718aba409 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/README.md @@ -0,0 +1,91 @@ +# Rust API for petstore-with-fake-endpoints-models-for-testing + +This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0.0 + + + + +This autogenerated project defines an API crate `petstore-with-fake-endpoints-models-for-testing` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl petstore-with-fake-endpoints-models-for-testing::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = petstore-with-fake-endpoints-models-for-testing::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/header.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/lib.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/lib.rs new file mode 100644 index 00000000000..4197ad3d2e8 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/lib.rs @@ -0,0 +1,679 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = "/v2"; +pub const API_VERSION: &str = "1.0.0"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestSpecialTagsResponse { + /// successful operation + SuccessfulOperation(models::Client), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum Call123exampleResponse { + /// success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FakeOuterBooleanSerializeResponse { + /// Output boolean + OutputBoolean(bool), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FakeOuterCompositeSerializeResponse { + /// Output composite + OutputComposite(models::OuterComposite), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FakeOuterNumberSerializeResponse { + /// Output number + OutputNumber(f64), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FakeOuterStringSerializeResponse { + /// Output string + OutputString(String), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FakeResponseWithNumericalDescriptionResponse { + /// 1234 + Status200, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum HyphenParamResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestBodyWithQueryParamsResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestClientModelResponse { + /// successful operation + SuccessfulOperation(models::Client), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum TestEndpointParametersResponse { + /// Invalid username supplied + InvalidUsernameSupplied, + /// User not found + UserNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum TestEnumParametersResponse { + /// Invalid request + InvalidRequest, + /// Not found + NotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestInlineAdditionalPropertiesResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestJsonFormDataResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TestClassnameResponse { + /// successful operation + SuccessfulOperation(models::Client), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum AddPetResponse { + /// Invalid input + InvalidInput, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum DeletePetResponse { + /// Invalid pet value + InvalidPetValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum FindPetsByStatusResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid status value + InvalidStatusValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum FindPetsByTagsResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid tag value + InvalidTagValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetPetByIdResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid ID supplied + InvalidIDSupplied, + /// Pet not found + PetNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum UpdatePetResponse { + /// Invalid ID supplied + InvalidIDSupplied, + /// Pet not found + PetNotFound, + /// Validation exception + ValidationException, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UpdatePetWithFormResponse { + /// Invalid input + InvalidInput, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UploadFileResponse { + /// successful operation + SuccessfulOperation(models::ApiResponse), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum DeleteOrderResponse { + /// Invalid ID supplied + InvalidIDSupplied, + /// Order not found + OrderNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GetInventoryResponse { + /// successful operation + SuccessfulOperation(std::collections::HashMap), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetOrderByIdResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid ID supplied + InvalidIDSupplied, + /// Order not found + OrderNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum PlaceOrderResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid Order + InvalidOrder, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUserResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUsersWithArrayInputResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUsersWithListInputResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum DeleteUserResponse { + /// Invalid username supplied + InvalidUsernameSupplied, + /// User not found + UserNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetUserByNameResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid username supplied + InvalidUsernameSupplied, + /// User not found + UserNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum LoginUserResponse { + /// successful operation + SuccessfulOperation { + body: String, + x_rate_limit: Option, + x_expires_after: Option>, + }, + /// Invalid username/password supplied + InvalidUsername, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum LogoutUserResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum UpdateUserResponse { + /// Invalid user supplied + InvalidUserSupplied, + /// User not found + UserNotFound, +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// To test special tags. + /// + /// TestSpecialTags - PATCH /v2/another-fake/dummy + async fn test_special_tags( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Client, + ) -> Result; + + /// Call123example - GET /v2/fake/operation-with-numeric-id + async fn call123example( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// FakeOuterBooleanSerialize - POST /v2/fake/outer/boolean + async fn fake_outer_boolean_serialize( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Option, + ) -> Result; + + /// FakeOuterCompositeSerialize - POST /v2/fake/outer/composite + async fn fake_outer_composite_serialize( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Option, + ) -> Result; + + /// FakeOuterNumberSerialize - POST /v2/fake/outer/number + async fn fake_outer_number_serialize( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Option, + ) -> Result; + + /// FakeOuterStringSerialize - POST /v2/fake/outer/string + async fn fake_outer_string_serialize( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Option, + ) -> Result; + + /// FakeResponseWithNumericalDescription - GET /v2/fake/response-with-numerical-description + async fn fake_response_with_numerical_description( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// HyphenParam - GET /v2/fake/hyphenParam/{hyphen-param} + async fn hyphen_param( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::HyphenParamPathParams, + ) -> Result; + + /// TestBodyWithQueryParams - PUT /v2/fake/body-with-query-params + async fn test_body_with_query_params( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::TestBodyWithQueryParamsQueryParams, + body: models::User, + ) -> Result; + + /// To test \"client\" model. + /// + /// TestClientModel - PATCH /v2/fake + async fn test_client_model( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Client, + ) -> Result; + + /// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트. + /// + /// TestEndpointParameters - POST /v2/fake + async fn test_endpoint_parameters( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// To test enum parameters. + /// + /// TestEnumParameters - GET /v2/fake + async fn test_enum_parameters( + &self, + method: Method, + host: Host, + cookies: CookieJar, + header_params: models::TestEnumParametersHeaderParams, + query_params: models::TestEnumParametersQueryParams, + ) -> Result; + + /// test inline additionalProperties. + /// + /// TestInlineAdditionalProperties - POST /v2/fake/inline-additionalProperties + async fn test_inline_additional_properties( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: std::collections::HashMap, + ) -> Result; + + /// test json serialization of form data. + /// + /// TestJsonFormData - GET /v2/fake/jsonFormData + async fn test_json_form_data( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// To test class name in snake case. + /// + /// TestClassname - PATCH /v2/fake_classname_test + async fn test_classname( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Client, + ) -> Result; + + /// Add a new pet to the store. + /// + /// AddPet - POST /v2/pet + async fn add_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Pet, + ) -> Result; + + /// Deletes a pet. + /// + /// DeletePet - DELETE /v2/pet/{petId} + async fn delete_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + header_params: models::DeletePetHeaderParams, + path_params: models::DeletePetPathParams, + ) -> Result; + + /// Finds Pets by status. + /// + /// FindPetsByStatus - GET /v2/pet/findByStatus + async fn find_pets_by_status( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::FindPetsByStatusQueryParams, + ) -> Result; + + /// Finds Pets by tags. + /// + /// FindPetsByTags - GET /v2/pet/findByTags + async fn find_pets_by_tags( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::FindPetsByTagsQueryParams, + ) -> Result; + + /// Find pet by ID. + /// + /// GetPetById - GET /v2/pet/{petId} + async fn get_pet_by_id( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetPetByIdPathParams, + ) -> Result; + + /// Update an existing pet. + /// + /// UpdatePet - PUT /v2/pet + async fn update_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Pet, + ) -> Result; + + /// Updates a pet in the store with form data. + /// + /// UpdatePetWithForm - POST /v2/pet/{petId} + async fn update_pet_with_form( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UpdatePetWithFormPathParams, + ) -> Result; + + /// uploads an image. + /// + /// UploadFile - POST /v2/pet/{petId}/uploadImage + async fn upload_file( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UploadFilePathParams, + body: Multipart, + ) -> Result; + + /// Delete purchase order by ID. + /// + /// DeleteOrder - DELETE /v2/store/order/{order_id} + async fn delete_order( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::DeleteOrderPathParams, + ) -> Result; + + /// Returns pet inventories by status. + /// + /// GetInventory - GET /v2/store/inventory + async fn get_inventory( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Find purchase order by ID. + /// + /// GetOrderById - GET /v2/store/order/{order_id} + async fn get_order_by_id( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetOrderByIdPathParams, + ) -> Result; + + /// Place an order for a pet. + /// + /// PlaceOrder - POST /v2/store/order + async fn place_order( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Order, + ) -> Result; + + /// Create user. + /// + /// CreateUser - POST /v2/user + async fn create_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::User, + ) -> Result; + + /// Creates list of users with given input array. + /// + /// CreateUsersWithArrayInput - POST /v2/user/createWithArray + async fn create_users_with_array_input( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Vec, + ) -> Result; + + /// Creates list of users with given input array. + /// + /// CreateUsersWithListInput - POST /v2/user/createWithList + async fn create_users_with_list_input( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Vec, + ) -> Result; + + /// Delete user. + /// + /// DeleteUser - DELETE /v2/user/{username} + async fn delete_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::DeleteUserPathParams, + ) -> Result; + + /// Get user by user name. + /// + /// GetUserByName - GET /v2/user/{username} + async fn get_user_by_name( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetUserByNamePathParams, + ) -> Result; + + /// Logs user into the system. + /// + /// LoginUser - GET /v2/user/login + async fn login_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::LoginUserQueryParams, + ) -> Result; + + /// Logs out current logged in user session. + /// + /// LogoutUser - GET /v2/user/logout + async fn logout_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Updated user. + /// + /// UpdateUser - PUT /v2/user/{username} + async fn update_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UpdateUserPathParams, + body: models::User, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs new file mode 100644 index 00000000000..78cda8e9db4 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs @@ -0,0 +1,5624 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct HyphenParamPathParams { + /// Parameter with hyphen in name + pub hyphen_param: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct TestBodyWithQueryParamsQueryParams { + #[serde(rename = "query")] + pub query: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct TestEnumParametersHeaderParams { + pub enum_header_string_array: Option>, + pub enum_header_string: Option, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct TestEnumParametersQueryParams { + /// Query parameter enum test (string array) + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_query_string_array")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_query_string_array: Option>, + /// Query parameter enum test (string) + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_query_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_query_string: Option, + /// Query parameter enum test (double) + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_query_integer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_query_integer: Option, + /// Query parameter enum test (double) + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_query_double")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_query_double: Option, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeletePetHeaderParams { + pub api_key: Option, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeletePetPathParams { + /// Pet id to delete + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct FindPetsByStatusQueryParams { + /// Status values that need to be considered for filter + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + pub status: Vec, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct FindPetsByTagsQueryParams { + /// Tags to filter by + #[serde(rename = "tags")] + pub tags: Vec, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetPetByIdPathParams { + /// ID of pet to return + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UpdatePetWithFormPathParams { + /// ID of pet that needs to be updated + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UploadFilePathParams { + /// ID of pet to update + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeleteOrderPathParams { + /// ID of the order that needs to be deleted + pub order_id: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetOrderByIdPathParams { + /// ID of pet that needs to be fetched + #[validate(range(min = 1, max = 5))] + pub order_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeleteUserPathParams { + /// The name that needs to be deleted + pub username: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetUserByNamePathParams { + /// The name that needs to be fetched. Use user1 for testing. + pub username: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct LoginUserQueryParams { + /// The user name for login + #[serde(rename = "username")] + pub username: String, + /// The password for login in clear text + #[serde(rename = "password")] + pub password: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UpdateUserPathParams { + /// name that need to be deleted + pub username: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AdditionalPropertiesClass { + #[serde(rename = "map_property")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map_property: Option>, + + #[serde(rename = "map_of_map_property")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map_of_map_property: + Option>>, +} + +impl AdditionalPropertiesClass { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AdditionalPropertiesClass { + AdditionalPropertiesClass { + map_property: None, + map_of_map_property: None, + } + } +} + +/// Converts the AdditionalPropertiesClass value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AdditionalPropertiesClass { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping map_property in query parameter serialization + + // Skipping map_of_map_property in query parameter serialization + // Skipping map_of_map_property in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AdditionalPropertiesClass value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AdditionalPropertiesClass { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub map_property: Vec>, + pub map_of_map_property: + Vec>>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AdditionalPropertiesClass".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "map_property" => return std::result::Result::Err("Parsing a container in this style is not supported in AdditionalPropertiesClass".to_string()), + "map_of_map_property" => return std::result::Result::Err("Parsing a container in this style is not supported in AdditionalPropertiesClass".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing AdditionalPropertiesClass".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AdditionalPropertiesClass { + map_property: intermediate_rep.map_property.into_iter().next(), + map_of_map_property: intermediate_rep.map_of_map_property.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AdditionalPropertiesClass - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AdditionalPropertiesClass - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Animal { + #[serde(rename = "className")] + pub class_name: String, + + #[serde(rename = "color")] + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, +} + +impl Animal { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(class_name: String) -> Animal { + Animal { + class_name, + color: Some("red".to_string()), + } + } +} + +/// Converts the Animal value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Animal { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("className".to_string()), + Some(self.class_name.to_string()), + self.color + .as_ref() + .map(|color| ["color".to_string(), color.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Animal value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Animal { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub class_name: Vec, + pub color: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Animal".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "className" => intermediate_rep.class_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "color" => intermediate_rep.color.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Animal".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Animal { + class_name: intermediate_rep + .class_name + .into_iter() + .next() + .ok_or_else(|| "className missing in Animal".to_string())?, + color: intermediate_rep.color.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Animal - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Animal - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnimalFarm(Vec); + +impl validator::Validate for AnimalFarm { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for AnimalFarm { + fn from(x: Vec) -> Self { + AnimalFarm(x) + } +} + +impl std::convert::From for Vec { + fn from(x: AnimalFarm) -> Self { + x.0 + } +} + +impl std::iter::FromIterator for AnimalFarm { + fn from_iter>(u: U) -> Self { + AnimalFarm(Vec::::from_iter(u)) + } +} + +impl std::iter::IntoIterator for AnimalFarm { + type Item = Animal; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a AnimalFarm { + type Item = &'a Animal; + type IntoIter = std::slice::Iter<'a, Animal>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> std::iter::IntoIterator for &'a mut AnimalFarm { + type Item = &'a mut Animal; + type IntoIter = std::slice::IterMut<'a, Animal>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl std::ops::Deref for AnimalFarm { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for AnimalFarm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Converts the AnimalFarm value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnimalFarm { + fn to_string(&self) -> String { + self.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnimalFarm value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnimalFarm { + type Err = ::Err; + + fn from_str(s: &str) -> std::result::Result { + let mut items = vec![]; + for item in s.split(',') { + items.push(item.parse()?); + } + std::result::Result::Ok(AnimalFarm(items)) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnimalFarm - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnimalFarm - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ApiResponse { + #[serde(rename = "code")] + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option, + + #[serde(rename = "type")] + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, + + #[serde(rename = "message")] + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +impl ApiResponse { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ApiResponse { + ApiResponse { + code: None, + r#type: None, + message: None, + } + } +} + +/// Converts the ApiResponse value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ApiResponse { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.code + .as_ref() + .map(|code| ["code".to_string(), code.to_string()].join(",")), + self.r#type + .as_ref() + .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")), + self.message + .as_ref() + .map(|message| ["message".to_string(), message.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ApiResponse value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ApiResponse { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub code: Vec, + pub r#type: Vec, + pub message: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ApiResponse".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "code" => intermediate_rep.code.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "message" => intermediate_rep.message.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ApiResponse".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ApiResponse { + code: intermediate_rep.code.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + message: intermediate_rep.message.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ApiResponse - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ApiResponse - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ArrayOfArrayOfNumberOnly { + #[serde(rename = "ArrayArrayNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_array_number: Option>>, +} + +impl ArrayOfArrayOfNumberOnly { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ArrayOfArrayOfNumberOnly { + ArrayOfArrayOfNumberOnly { + array_array_number: None, + } + } +} + +/// Converts the ArrayOfArrayOfNumberOnly value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ArrayOfArrayOfNumberOnly { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping ArrayArrayNumber in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ArrayOfArrayOfNumberOnly value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ArrayOfArrayOfNumberOnly { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub array_array_number: Vec>>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ArrayOfArrayOfNumberOnly".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "ArrayArrayNumber" => return std::result::Result::Err("Parsing a container in this style is not supported in ArrayOfArrayOfNumberOnly".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing ArrayOfArrayOfNumberOnly".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ArrayOfArrayOfNumberOnly { + array_array_number: intermediate_rep.array_array_number.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ArrayOfArrayOfNumberOnly - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ArrayOfArrayOfNumberOnly - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ArrayOfNumberOnly { + #[serde(rename = "ArrayNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_number: Option>, +} + +impl ArrayOfNumberOnly { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ArrayOfNumberOnly { + ArrayOfNumberOnly { array_number: None } + } +} + +/// Converts the ArrayOfNumberOnly value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ArrayOfNumberOnly { + fn to_string(&self) -> String { + let params: Vec> = vec![self.array_number.as_ref().map(|array_number| { + [ + "ArrayNumber".to_string(), + array_number + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + })]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ArrayOfNumberOnly value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ArrayOfNumberOnly { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub array_number: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ArrayOfNumberOnly".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "ArrayNumber" => return std::result::Result::Err( + "Parsing a container in this style is not supported in ArrayOfNumberOnly" + .to_string(), + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ArrayOfNumberOnly".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ArrayOfNumberOnly { + array_number: intermediate_rep.array_number.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ArrayOfNumberOnly - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ArrayOfNumberOnly - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ArrayTest { + #[serde(rename = "array_of_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_of_string: Option>, + + #[serde(rename = "array_array_of_integer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_array_of_integer: Option>>, + + #[serde(rename = "array_array_of_model")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_array_of_model: Option>>, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "array_of_enum")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_of_enum: Option>, +} + +impl ArrayTest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ArrayTest { + ArrayTest { + array_of_string: None, + array_array_of_integer: None, + array_array_of_model: None, + array_of_enum: None, + } + } +} + +/// Converts the ArrayTest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ArrayTest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.array_of_string.as_ref().map(|array_of_string| { + [ + "array_of_string".to_string(), + array_of_string + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + // Skipping array_array_of_integer in query parameter serialization + + // Skipping array_array_of_model in query parameter serialization + self.array_of_enum.as_ref().map(|array_of_enum| { + [ + "array_of_enum".to_string(), + array_of_enum + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ArrayTest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ArrayTest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub array_of_string: Vec>, + pub array_array_of_integer: Vec>>, + pub array_array_of_model: Vec>>, + pub array_of_enum: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ArrayTest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "array_of_string" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in ArrayTest" + .to_string(), + ) + } + "array_array_of_integer" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in ArrayTest" + .to_string(), + ) + } + "array_array_of_model" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in ArrayTest" + .to_string(), + ) + } + "array_of_enum" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in ArrayTest" + .to_string(), + ) + } + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ArrayTest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ArrayTest { + array_of_string: intermediate_rep.array_of_string.into_iter().next(), + array_array_of_integer: intermediate_rep.array_array_of_integer.into_iter().next(), + array_array_of_model: intermediate_rep.array_array_of_model.into_iter().next(), + array_of_enum: intermediate_rep.array_of_enum.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ArrayTest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ArrayTest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Capitalization { + #[serde(rename = "smallCamel")] + #[serde(skip_serializing_if = "Option::is_none")] + pub small_camel: Option, + + #[serde(rename = "CapitalCamel")] + #[serde(skip_serializing_if = "Option::is_none")] + pub capital_camel: Option, + + #[serde(rename = "small_Snake")] + #[serde(skip_serializing_if = "Option::is_none")] + pub small_snake: Option, + + #[serde(rename = "Capital_Snake")] + #[serde(skip_serializing_if = "Option::is_none")] + pub capital_snake: Option, + + #[serde(rename = "SCA_ETH_Flow_Points")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sca_eth_flow_points: Option, + + /// Name of the pet + #[serde(rename = "ATT_NAME")] + #[serde(skip_serializing_if = "Option::is_none")] + pub att_name: Option, +} + +impl Capitalization { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Capitalization { + Capitalization { + small_camel: None, + capital_camel: None, + small_snake: None, + capital_snake: None, + sca_eth_flow_points: None, + att_name: None, + } + } +} + +/// Converts the Capitalization value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Capitalization { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.small_camel + .as_ref() + .map(|small_camel| ["smallCamel".to_string(), small_camel.to_string()].join(",")), + self.capital_camel.as_ref().map(|capital_camel| { + ["CapitalCamel".to_string(), capital_camel.to_string()].join(",") + }), + self.small_snake + .as_ref() + .map(|small_snake| ["small_Snake".to_string(), small_snake.to_string()].join(",")), + self.capital_snake.as_ref().map(|capital_snake| { + ["Capital_Snake".to_string(), capital_snake.to_string()].join(",") + }), + self.sca_eth_flow_points + .as_ref() + .map(|sca_eth_flow_points| { + [ + "SCA_ETH_Flow_Points".to_string(), + sca_eth_flow_points.to_string(), + ] + .join(",") + }), + self.att_name + .as_ref() + .map(|att_name| ["ATT_NAME".to_string(), att_name.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Capitalization value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Capitalization { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub small_camel: Vec, + pub capital_camel: Vec, + pub small_snake: Vec, + pub capital_snake: Vec, + pub sca_eth_flow_points: Vec, + pub att_name: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Capitalization".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "smallCamel" => intermediate_rep.small_camel.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "CapitalCamel" => intermediate_rep.capital_camel.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "small_Snake" => intermediate_rep.small_snake.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "Capital_Snake" => intermediate_rep.capital_snake.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "SCA_ETH_Flow_Points" => intermediate_rep.sca_eth_flow_points.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "ATT_NAME" => intermediate_rep.att_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Capitalization".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Capitalization { + small_camel: intermediate_rep.small_camel.into_iter().next(), + capital_camel: intermediate_rep.capital_camel.into_iter().next(), + small_snake: intermediate_rep.small_snake.into_iter().next(), + capital_snake: intermediate_rep.capital_snake.into_iter().next(), + sca_eth_flow_points: intermediate_rep.sca_eth_flow_points.into_iter().next(), + att_name: intermediate_rep.att_name.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Capitalization - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Capitalization - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Cat { + #[serde(rename = "className")] + pub class_name: String, + + #[serde(rename = "color")] + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, + + #[serde(rename = "declawed")] + #[serde(skip_serializing_if = "Option::is_none")] + pub declawed: Option, +} + +impl Cat { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(class_name: String) -> Cat { + Cat { + class_name, + color: Some("red".to_string()), + declawed: None, + } + } +} + +/// Converts the Cat value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Cat { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("className".to_string()), + Some(self.class_name.to_string()), + self.color + .as_ref() + .map(|color| ["color".to_string(), color.to_string()].join(",")), + self.declawed + .as_ref() + .map(|declawed| ["declawed".to_string(), declawed.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Cat value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Cat { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub class_name: Vec, + pub color: Vec, + pub declawed: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Cat".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "className" => intermediate_rep.class_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "color" => intermediate_rep.color.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "declawed" => intermediate_rep.declawed.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Cat".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Cat { + class_name: intermediate_rep + .class_name + .into_iter() + .next() + .ok_or_else(|| "className missing in Cat".to_string())?, + color: intermediate_rep.color.into_iter().next(), + declawed: intermediate_rep.declawed.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Cat - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Cat - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Category { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +impl Category { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Category { + Category { + id: None, + name: None, + } + } +} + +/// Converts the Category value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Category { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Category value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Category { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub name: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Category".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Category".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Category { + id: intermediate_rep.id.into_iter().next(), + name: intermediate_rep.name.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Category - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Category - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Model for testing model with \"_class\" property + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ClassModel { + #[serde(rename = "_class")] + #[serde(skip_serializing_if = "Option::is_none")] + pub _class: Option, +} + +impl ClassModel { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ClassModel { + ClassModel { _class: None } + } +} + +/// Converts the ClassModel value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ClassModel { + fn to_string(&self) -> String { + let params: Vec> = vec![self + ._class + .as_ref() + .map(|_class| ["_class".to_string(), _class.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ClassModel value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ClassModel { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub _class: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ClassModel".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "_class" => intermediate_rep._class.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ClassModel".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ClassModel { + _class: intermediate_rep._class.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ClassModel - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ClassModel - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Client { + #[serde(rename = "client")] + #[serde(skip_serializing_if = "Option::is_none")] + pub client: Option, +} + +impl Client { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Client { + Client { client: None } + } +} + +/// Converts the Client value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Client { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .client + .as_ref() + .map(|client| ["client".to_string(), client.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Client value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Client { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub client: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Client".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "client" => intermediate_rep.client.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Client".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Client { + client: intermediate_rep.client.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Client - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Client - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Dog { + #[serde(rename = "className")] + pub class_name: String, + + #[serde(rename = "color")] + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, + + #[serde(rename = "breed")] + #[serde(skip_serializing_if = "Option::is_none")] + pub breed: Option, +} + +impl Dog { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(class_name: String) -> Dog { + Dog { + class_name, + color: Some("red".to_string()), + breed: None, + } + } +} + +/// Converts the Dog value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Dog { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("className".to_string()), + Some(self.class_name.to_string()), + self.color + .as_ref() + .map(|color| ["color".to_string(), color.to_string()].join(",")), + self.breed + .as_ref() + .map(|breed| ["breed".to_string(), breed.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Dog value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Dog { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub class_name: Vec, + pub color: Vec, + pub breed: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Dog".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "className" => intermediate_rep.class_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "color" => intermediate_rep.color.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "breed" => intermediate_rep.breed.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Dog".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Dog { + class_name: intermediate_rep + .class_name + .into_iter() + .next() + .ok_or_else(|| "className missing in Dog".to_string())?, + color: intermediate_rep.color.into_iter().next(), + breed: intermediate_rep.breed.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Dog - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Dog - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + #[serde(rename = "$special[property.name]")] + #[serde(skip_serializing_if = "Option::is_none")] + pub dollar_special_left_square_bracket_property_period_name_right_square_bracket: Option, +} + +impl DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + dollar_special_left_square_bracket_property_period_name_right_square_bracket: None, + } + } +} + +/// Converts the DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + fn to_string(&self) -> String { + let params: Vec> = vec![ + + self.dollar_special_left_square_bracket_property_period_name_right_square_bracket.as_ref().map(|dollar_special_left_square_bracket_property_period_name_right_square_bracket| { + [ + "$special[property.name]".to_string(), + dollar_special_left_square_bracket_property_period_name_right_square_bracket.to_string(), + ].join(",") + }), + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub dollar_special_left_square_bracket_property_period_name_right_square_bracket: + Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "$special[property.name]" => intermediate_rep.dollar_special_left_square_bracket_property_period_name_right_square_bracket.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { + dollar_special_left_square_bracket_property_period_name_right_square_bracket: intermediate_rep.dollar_special_left_square_bracket_property_period_name_right_square_bracket.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl + std::convert::TryFrom< + header::IntoHeaderValue, + > for HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue< + DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket, + >, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct EnumArrays { + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "just_symbol")] + #[serde(skip_serializing_if = "Option::is_none")] + pub just_symbol: Option, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "array_enum")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_enum: Option>, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "array_array_enum")] + #[serde(skip_serializing_if = "Option::is_none")] + pub array_array_enum: Option>>, +} + +impl EnumArrays { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> EnumArrays { + EnumArrays { + just_symbol: None, + array_enum: None, + array_array_enum: None, + } + } +} + +/// Converts the EnumArrays value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for EnumArrays { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.just_symbol + .as_ref() + .map(|just_symbol| ["just_symbol".to_string(), just_symbol.to_string()].join(",")), + self.array_enum.as_ref().map(|array_enum| { + [ + "array_enum".to_string(), + array_enum + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + // Skipping array_array_enum in query parameter serialization + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a EnumArrays value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for EnumArrays { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub just_symbol: Vec, + pub array_enum: Vec>, + pub array_array_enum: Vec>>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing EnumArrays".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "just_symbol" => intermediate_rep.just_symbol.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + "array_enum" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in EnumArrays" + .to_string(), + ) + } + "array_array_enum" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in EnumArrays" + .to_string(), + ) + } + _ => { + return std::result::Result::Err( + "Unexpected key while parsing EnumArrays".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(EnumArrays { + just_symbol: intermediate_rep.just_symbol.into_iter().next(), + array_enum: intermediate_rep.array_enum.into_iter().next(), + array_array_enum: intermediate_rep.array_array_enum.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for EnumArrays - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into EnumArrays - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))] +pub enum EnumClass { + #[serde(rename = "_abc")] + Abc, + #[serde(rename = "-efg")] + Efg, + #[serde(rename = "(xyz)")] + LeftParenthesisXyzRightParenthesis, +} + +impl std::fmt::Display for EnumClass { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + EnumClass::Abc => write!(f, "_abc"), + EnumClass::Efg => write!(f, "-efg"), + EnumClass::LeftParenthesisXyzRightParenthesis => write!(f, "(xyz)"), + } + } +} + +impl std::str::FromStr for EnumClass { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + "_abc" => std::result::Result::Ok(EnumClass::Abc), + "-efg" => std::result::Result::Ok(EnumClass::Efg), + "(xyz)" => std::result::Result::Ok(EnumClass::LeftParenthesisXyzRightParenthesis), + _ => std::result::Result::Err(format!("Value not valid: {}", s)), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct EnumTest { + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_string: Option, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_string_required")] + pub enum_string_required: String, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_integer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_integer: Option, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "enum_number")] + #[serde(skip_serializing_if = "Option::is_none")] + pub enum_number: Option, + + #[serde(rename = "outerEnum")] + #[serde(skip_serializing_if = "Option::is_none")] + pub outer_enum: Option, +} + +impl EnumTest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(enum_string_required: String) -> EnumTest { + EnumTest { + enum_string: None, + enum_string_required, + enum_integer: None, + enum_number: None, + outer_enum: None, + } + } +} + +/// Converts the EnumTest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for EnumTest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.enum_string + .as_ref() + .map(|enum_string| ["enum_string".to_string(), enum_string.to_string()].join(",")), + Some("enum_string_required".to_string()), + Some(self.enum_string_required.to_string()), + self.enum_integer.as_ref().map(|enum_integer| { + ["enum_integer".to_string(), enum_integer.to_string()].join(",") + }), + self.enum_number + .as_ref() + .map(|enum_number| ["enum_number".to_string(), enum_number.to_string()].join(",")), + // Skipping outerEnum in query parameter serialization + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a EnumTest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for EnumTest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub enum_string: Vec, + pub enum_string_required: Vec, + pub enum_integer: Vec, + pub enum_number: Vec, + pub outer_enum: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing EnumTest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "enum_string" => intermediate_rep.enum_string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "enum_string_required" => intermediate_rep.enum_string_required.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "enum_integer" => intermediate_rep.enum_integer.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "enum_number" => intermediate_rep.enum_number.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "outerEnum" => intermediate_rep.outer_enum.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing EnumTest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(EnumTest { + enum_string: intermediate_rep.enum_string.into_iter().next(), + enum_string_required: intermediate_rep + .enum_string_required + .into_iter() + .next() + .ok_or_else(|| "enum_string_required missing in EnumTest".to_string())?, + enum_integer: intermediate_rep.enum_integer.into_iter().next(), + enum_number: intermediate_rep.enum_number.into_iter().next(), + outer_enum: intermediate_rep.outer_enum.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for EnumTest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into EnumTest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct FormatTest { + #[serde(rename = "integer")] + #[validate(range(min = 10, max = 100))] + #[serde(skip_serializing_if = "Option::is_none")] + pub integer: Option, + + #[serde(rename = "int32")] + #[validate(range(min = 20, max = 200))] + #[serde(skip_serializing_if = "Option::is_none")] + pub int32: Option, + + #[serde(rename = "int64")] + #[serde(skip_serializing_if = "Option::is_none")] + pub int64: Option, + + #[serde(rename = "number")] + #[validate(range(min = 32.1, max = 543.2))] + pub number: f64, + + #[serde(rename = "float")] + #[validate(range(min = 54.3, max = 987.6))] + #[serde(skip_serializing_if = "Option::is_none")] + pub float: Option, + + #[serde(rename = "double")] + #[validate(range(min = 67.8, max = 123.4))] + #[serde(skip_serializing_if = "Option::is_none")] + pub double: Option, + + #[serde(rename = "string")] + #[validate(regex = "RE_FORMATTEST_STRING")] + #[serde(skip_serializing_if = "Option::is_none")] + pub string: Option, + + #[serde(rename = "byte")] + #[validate(custom = "validate_byte_formattest_byte")] + pub byte: ByteArray, + + #[serde(rename = "binary")] + #[serde(skip_serializing_if = "Option::is_none")] + pub binary: Option, + + #[serde(rename = "date")] + pub date: chrono::naive::NaiveDate, + + #[serde(rename = "dateTime")] + #[serde(skip_serializing_if = "Option::is_none")] + pub date_time: Option>, + + #[serde(rename = "uuid")] + #[serde(skip_serializing_if = "Option::is_none")] + pub uuid: Option, + + #[serde(rename = "password")] + #[validate(length(min = 10, max = 64))] + pub password: String, +} + +lazy_static::lazy_static! { + static ref RE_FORMATTEST_STRING: regex::Regex = regex::Regex::new(r"/[a-z]/i").unwrap(); +} +lazy_static::lazy_static! { + static ref RE_FORMATTEST_BYTE: regex::bytes::Regex = regex::bytes::Regex::new(r"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$").unwrap(); +} +fn validate_byte_formattest_byte( + b: &ByteArray, +) -> std::result::Result<(), validator::ValidationError> { + if !RE_FORMATTEST_BYTE.is_match(&b.0) { + return Err(validator::ValidationError::new("Character not allowed")); + } + std::result::Result::Ok(()) +} + +impl FormatTest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new( + number: f64, + byte: ByteArray, + date: chrono::naive::NaiveDate, + password: String, + ) -> FormatTest { + FormatTest { + integer: None, + int32: None, + int64: None, + number, + float: None, + double: None, + string: None, + byte, + binary: None, + date, + date_time: None, + uuid: None, + password, + } + } +} + +/// Converts the FormatTest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for FormatTest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.integer + .as_ref() + .map(|integer| ["integer".to_string(), integer.to_string()].join(",")), + self.int32 + .as_ref() + .map(|int32| ["int32".to_string(), int32.to_string()].join(",")), + self.int64 + .as_ref() + .map(|int64| ["int64".to_string(), int64.to_string()].join(",")), + Some("number".to_string()), + Some(self.number.to_string()), + self.float + .as_ref() + .map(|float| ["float".to_string(), float.to_string()].join(",")), + self.double + .as_ref() + .map(|double| ["double".to_string(), double.to_string()].join(",")), + self.string + .as_ref() + .map(|string| ["string".to_string(), string.to_string()].join(",")), + // Skipping byte in query parameter serialization + // Skipping byte in query parameter serialization + + // Skipping binary in query parameter serialization + // Skipping binary in query parameter serialization + + // Skipping date in query parameter serialization + + // Skipping dateTime in query parameter serialization + + // Skipping uuid in query parameter serialization + Some("password".to_string()), + Some(self.password.to_string()), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a FormatTest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for FormatTest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub integer: Vec, + pub int32: Vec, + pub int64: Vec, + pub number: Vec, + pub float: Vec, + pub double: Vec, + pub string: Vec, + pub byte: Vec, + pub binary: Vec, + pub date: Vec, + pub date_time: Vec>, + pub uuid: Vec, + pub password: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing FormatTest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "integer" => intermediate_rep + .integer + .push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "int32" => intermediate_rep.int32.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "int64" => intermediate_rep.int64.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "number" => intermediate_rep.number.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "float" => intermediate_rep.float.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "double" => intermediate_rep.double.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "string" => intermediate_rep.string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + "byte" => { + return std::result::Result::Err( + "Parsing binary data in this style is not supported in FormatTest" + .to_string(), + ) + } + "binary" => { + return std::result::Result::Err( + "Parsing binary data in this style is not supported in FormatTest" + .to_string(), + ) + } + #[allow(clippy::redundant_clone)] + "date" => intermediate_rep.date.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "dateTime" => intermediate_rep.date_time.push( + as std::str::FromStr>::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "uuid" => intermediate_rep.uuid.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "password" => intermediate_rep.password.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing FormatTest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(FormatTest { + integer: intermediate_rep.integer.into_iter().next(), + int32: intermediate_rep.int32.into_iter().next(), + int64: intermediate_rep.int64.into_iter().next(), + number: intermediate_rep + .number + .into_iter() + .next() + .ok_or_else(|| "number missing in FormatTest".to_string())?, + float: intermediate_rep.float.into_iter().next(), + double: intermediate_rep.double.into_iter().next(), + string: intermediate_rep.string.into_iter().next(), + byte: intermediate_rep + .byte + .into_iter() + .next() + .ok_or_else(|| "byte missing in FormatTest".to_string())?, + binary: intermediate_rep.binary.into_iter().next(), + date: intermediate_rep + .date + .into_iter() + .next() + .ok_or_else(|| "date missing in FormatTest".to_string())?, + date_time: intermediate_rep.date_time.into_iter().next(), + uuid: intermediate_rep.uuid.into_iter().next(), + password: intermediate_rep + .password + .into_iter() + .next() + .ok_or_else(|| "password missing in FormatTest".to_string())?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for FormatTest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into FormatTest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct HasOnlyReadOnly { + #[serde(rename = "bar")] + #[serde(skip_serializing_if = "Option::is_none")] + pub bar: Option, + + #[serde(rename = "foo")] + #[serde(skip_serializing_if = "Option::is_none")] + pub foo: Option, +} + +impl HasOnlyReadOnly { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> HasOnlyReadOnly { + HasOnlyReadOnly { + bar: None, + foo: None, + } + } +} + +/// Converts the HasOnlyReadOnly value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for HasOnlyReadOnly { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.bar + .as_ref() + .map(|bar| ["bar".to_string(), bar.to_string()].join(",")), + self.foo + .as_ref() + .map(|foo| ["foo".to_string(), foo.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a HasOnlyReadOnly value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for HasOnlyReadOnly { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub bar: Vec, + pub foo: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing HasOnlyReadOnly".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "bar" => intermediate_rep.bar.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "foo" => intermediate_rep.foo.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing HasOnlyReadOnly".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(HasOnlyReadOnly { + bar: intermediate_rep.bar.into_iter().next(), + foo: intermediate_rep.foo.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for HasOnlyReadOnly - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into HasOnlyReadOnly - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct List { + #[serde(rename = "123-list")] + #[serde(skip_serializing_if = "Option::is_none")] + pub param_123_list: Option, +} + +impl List { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> List { + List { + param_123_list: None, + } + } +} + +/// Converts the List value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for List { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .param_123_list + .as_ref() + .map(|param_123_list| ["123-list".to_string(), param_123_list.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a List value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for List { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub param_123_list: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing List".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "123-list" => intermediate_rep.param_123_list.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing List".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(List { + param_123_list: intermediate_rep.param_123_list.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for List - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into List - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MapTest { + #[serde(rename = "map_map_of_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map_map_of_string: + Option>>, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "map_map_of_enum")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map_map_of_enum: + Option>>, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "map_of_enum_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map_of_enum_string: Option>, +} + +impl MapTest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> MapTest { + MapTest { + map_map_of_string: None, + map_map_of_enum: None, + map_of_enum_string: None, + } + } +} + +/// Converts the MapTest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MapTest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping map_map_of_string in query parameter serialization + // Skipping map_map_of_string in query parameter serialization + + // Skipping map_map_of_enum in query parameter serialization + // Skipping map_map_of_enum in query parameter serialization + + // Skipping map_of_enum_string in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MapTest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MapTest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub map_map_of_string: + Vec>>, + pub map_map_of_enum: + Vec>>, + pub map_of_enum_string: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing MapTest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "map_map_of_string" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in MapTest" + .to_string(), + ) + } + "map_map_of_enum" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in MapTest" + .to_string(), + ) + } + "map_of_enum_string" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in MapTest" + .to_string(), + ) + } + _ => { + return std::result::Result::Err( + "Unexpected key while parsing MapTest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MapTest { + map_map_of_string: intermediate_rep.map_map_of_string.into_iter().next(), + map_map_of_enum: intermediate_rep.map_map_of_enum.into_iter().next(), + map_of_enum_string: intermediate_rep.map_of_enum_string.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for MapTest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into MapTest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct MixedPropertiesAndAdditionalPropertiesClass { + #[serde(rename = "uuid")] + #[serde(skip_serializing_if = "Option::is_none")] + pub uuid: Option, + + #[serde(rename = "dateTime")] + #[serde(skip_serializing_if = "Option::is_none")] + pub date_time: Option>, + + #[serde(rename = "map")] + #[serde(skip_serializing_if = "Option::is_none")] + pub map: Option>, +} + +impl MixedPropertiesAndAdditionalPropertiesClass { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> MixedPropertiesAndAdditionalPropertiesClass { + MixedPropertiesAndAdditionalPropertiesClass { + uuid: None, + date_time: None, + map: None, + } + } +} + +/// Converts the MixedPropertiesAndAdditionalPropertiesClass value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for MixedPropertiesAndAdditionalPropertiesClass { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping uuid in query parameter serialization + + // Skipping dateTime in query parameter serialization + + // Skipping map in query parameter serialization + // Skipping map in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a MixedPropertiesAndAdditionalPropertiesClass value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for MixedPropertiesAndAdditionalPropertiesClass { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub uuid: Vec, + pub date_time: Vec>, + pub map: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = + match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err( + "Missing value while parsing MixedPropertiesAndAdditionalPropertiesClass" + .to_string(), + ), + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "uuid" => intermediate_rep.uuid.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "dateTime" => intermediate_rep.date_time.push( as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + "map" => return std::result::Result::Err("Parsing a container in this style is not supported in MixedPropertiesAndAdditionalPropertiesClass".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing MixedPropertiesAndAdditionalPropertiesClass".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(MixedPropertiesAndAdditionalPropertiesClass { + uuid: intermediate_rep.uuid.into_iter().next(), + date_time: intermediate_rep.date_time.into_iter().next(), + map: intermediate_rep.map.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> + for HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for MixedPropertiesAndAdditionalPropertiesClass - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into MixedPropertiesAndAdditionalPropertiesClass - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +/// Model for testing model name starting with number + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Model200Response { + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(rename = "class")] + #[serde(skip_serializing_if = "Option::is_none")] + pub class: Option, +} + +impl Model200Response { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Model200Response { + Model200Response { + name: None, + class: None, + } + } +} + +/// Converts the Model200Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Model200Response { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + self.class + .as_ref() + .map(|class| ["class".to_string(), class.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Model200Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Model200Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub name: Vec, + pub class: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Model200Response".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "class" => intermediate_rep.class.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Model200Response".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Model200Response { + name: intermediate_rep.name.into_iter().next(), + class: intermediate_rep.class.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Model200Response - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Model200Response - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Model for testing model name same as property name + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Name { + #[serde(rename = "name")] + pub name: i32, + + #[serde(rename = "snake_case")] + #[serde(skip_serializing_if = "Option::is_none")] + pub snake_case: Option, + + #[serde(rename = "property")] + #[serde(skip_serializing_if = "Option::is_none")] + pub property: Option, + + #[serde(rename = "123Number")] + #[serde(skip_serializing_if = "Option::is_none")] + pub param_123_number: Option, +} + +impl Name { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(name: i32) -> Name { + Name { + name, + snake_case: None, + property: None, + param_123_number: None, + } + } +} + +/// Converts the Name value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Name { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("name".to_string()), + Some(self.name.to_string()), + self.snake_case + .as_ref() + .map(|snake_case| ["snake_case".to_string(), snake_case.to_string()].join(",")), + self.property + .as_ref() + .map(|property| ["property".to_string(), property.to_string()].join(",")), + self.param_123_number.as_ref().map(|param_123_number| { + ["123Number".to_string(), param_123_number.to_string()].join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Name value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Name { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub name: Vec, + pub snake_case: Vec, + pub property: Vec, + pub param_123_number: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Name".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "snake_case" => intermediate_rep.snake_case.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "property" => intermediate_rep.property.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "123Number" => intermediate_rep.param_123_number.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Name".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Name { + name: intermediate_rep + .name + .into_iter() + .next() + .ok_or_else(|| "name missing in Name".to_string())?, + snake_case: intermediate_rep.snake_case.into_iter().next(), + property: intermediate_rep.property.into_iter().next(), + param_123_number: intermediate_rep.param_123_number.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Name - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Name - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct NumberOnly { + #[serde(rename = "JustNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub just_number: Option, +} + +impl NumberOnly { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> NumberOnly { + NumberOnly { just_number: None } + } +} + +/// Converts the NumberOnly value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for NumberOnly { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .just_number + .as_ref() + .map(|just_number| ["JustNumber".to_string(), just_number.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a NumberOnly value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for NumberOnly { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub just_number: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing NumberOnly".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "JustNumber" => intermediate_rep.just_number.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing NumberOnly".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(NumberOnly { + just_number: intermediate_rep.just_number.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for NumberOnly - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into NumberOnly - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectContainingObjectWithOnlyAdditionalProperties { + #[serde(rename = "inner")] + #[serde(skip_serializing_if = "Option::is_none")] + pub inner: Option, +} + +impl ObjectContainingObjectWithOnlyAdditionalProperties { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ObjectContainingObjectWithOnlyAdditionalProperties { + ObjectContainingObjectWithOnlyAdditionalProperties { inner: None } + } +} + +/// Converts the ObjectContainingObjectWithOnlyAdditionalProperties value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectContainingObjectWithOnlyAdditionalProperties { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping inner in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectContainingObjectWithOnlyAdditionalProperties value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectContainingObjectWithOnlyAdditionalProperties { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inner: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing ObjectContainingObjectWithOnlyAdditionalProperties".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "inner" => intermediate_rep.inner.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing ObjectContainingObjectWithOnlyAdditionalProperties".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectContainingObjectWithOnlyAdditionalProperties { + inner: intermediate_rep.inner.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl + std::convert::TryFrom< + header::IntoHeaderValue, + > for HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for ObjectContainingObjectWithOnlyAdditionalProperties - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into ObjectContainingObjectWithOnlyAdditionalProperties - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectWithOnlyAdditionalProperties(std::collections::HashMap); + +impl validator::Validate for ObjectWithOnlyAdditionalProperties { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> + for ObjectWithOnlyAdditionalProperties +{ + fn from(x: std::collections::HashMap) -> Self { + ObjectWithOnlyAdditionalProperties(x) + } +} + +impl std::convert::From + for std::collections::HashMap +{ + fn from(x: ObjectWithOnlyAdditionalProperties) -> Self { + x.0 + } +} + +impl std::ops::Deref for ObjectWithOnlyAdditionalProperties { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } +} + +impl std::ops::DerefMut for ObjectWithOnlyAdditionalProperties { + fn deref_mut(&mut self) -> &mut std::collections::HashMap { + &mut self.0 + } +} + +/// Converts the ObjectWithOnlyAdditionalProperties value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl ::std::string::ToString for ObjectWithOnlyAdditionalProperties { + fn to_string(&self) -> String { + // Skipping additionalProperties in query parameter serialization + "".to_string() + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectWithOnlyAdditionalProperties value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl ::std::str::FromStr for ObjectWithOnlyAdditionalProperties { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Err( + "Parsing additionalProperties for ObjectWithOnlyAdditionalProperties is not supported", + ) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Order { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "petId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub pet_id: Option, + + #[serde(rename = "quantity")] + #[serde(skip_serializing_if = "Option::is_none")] + pub quantity: Option, + + #[serde(rename = "shipDate")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ship_date: Option>, + + /// Order Status + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + + #[serde(rename = "complete")] + #[serde(skip_serializing_if = "Option::is_none")] + pub complete: Option, +} + +impl Order { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Order { + Order { + id: None, + pet_id: None, + quantity: None, + ship_date: None, + status: None, + complete: Some(false), + } + } +} + +/// Converts the Order value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Order { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.pet_id + .as_ref() + .map(|pet_id| ["petId".to_string(), pet_id.to_string()].join(",")), + self.quantity + .as_ref() + .map(|quantity| ["quantity".to_string(), quantity.to_string()].join(",")), + // Skipping shipDate in query parameter serialization + self.status + .as_ref() + .map(|status| ["status".to_string(), status.to_string()].join(",")), + self.complete + .as_ref() + .map(|complete| ["complete".to_string(), complete.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Order value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Order { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub pet_id: Vec, + pub quantity: Vec, + pub ship_date: Vec>, + pub status: Vec, + pub complete: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Order".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "petId" => intermediate_rep.pet_id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "quantity" => intermediate_rep.quantity.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "shipDate" => intermediate_rep.ship_date.push( + as std::str::FromStr>::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "status" => intermediate_rep.status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "complete" => intermediate_rep.complete.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Order".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Order { + id: intermediate_rep.id.into_iter().next(), + pet_id: intermediate_rep.pet_id.into_iter().next(), + quantity: intermediate_rep.quantity.into_iter().next(), + ship_date: intermediate_rep.ship_date.into_iter().next(), + status: intermediate_rep.status.into_iter().next(), + complete: intermediate_rep.complete.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Order - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Order - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct OuterBoolean(bool); + +impl validator::Validate for OuterBoolean { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for OuterBoolean { + fn from(x: bool) -> Self { + OuterBoolean(x) + } +} + +impl std::convert::From for bool { + fn from(x: OuterBoolean) -> Self { + x.0 + } +} + +impl std::ops::Deref for OuterBoolean { + type Target = bool; + fn deref(&self) -> &bool { + &self.0 + } +} + +impl std::ops::DerefMut for OuterBoolean { + fn deref_mut(&mut self) -> &mut bool { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct OuterComposite { + #[serde(rename = "my_number")] + #[serde(skip_serializing_if = "Option::is_none")] + pub my_number: Option, + + #[serde(rename = "my_string")] + #[serde(skip_serializing_if = "Option::is_none")] + pub my_string: Option, + + #[serde(rename = "my_boolean")] + #[serde(skip_serializing_if = "Option::is_none")] + pub my_boolean: Option, +} + +impl OuterComposite { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> OuterComposite { + OuterComposite { + my_number: None, + my_string: None, + my_boolean: None, + } + } +} + +/// Converts the OuterComposite value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for OuterComposite { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.my_number + .as_ref() + .map(|my_number| ["my_number".to_string(), my_number.to_string()].join(",")), + self.my_string + .as_ref() + .map(|my_string| ["my_string".to_string(), my_string.to_string()].join(",")), + self.my_boolean + .as_ref() + .map(|my_boolean| ["my_boolean".to_string(), my_boolean.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a OuterComposite value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for OuterComposite { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub my_number: Vec, + pub my_string: Vec, + pub my_boolean: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing OuterComposite".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "my_number" => intermediate_rep.my_number.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "my_string" => intermediate_rep.my_string.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "my_boolean" => intermediate_rep.my_boolean.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing OuterComposite".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(OuterComposite { + my_number: intermediate_rep.my_number.into_iter().next(), + my_string: intermediate_rep.my_string.into_iter().next(), + my_boolean: intermediate_rep.my_boolean.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for OuterComposite - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into OuterComposite - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))] +pub enum OuterEnum { + #[serde(rename = "placed")] + Placed, + #[serde(rename = "approved")] + Approved, + #[serde(rename = "delivered")] + Delivered, +} + +impl std::fmt::Display for OuterEnum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + OuterEnum::Placed => write!(f, "placed"), + OuterEnum::Approved => write!(f, "approved"), + OuterEnum::Delivered => write!(f, "delivered"), + } + } +} + +impl std::str::FromStr for OuterEnum { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + match s { + "placed" => std::result::Result::Ok(OuterEnum::Placed), + "approved" => std::result::Result::Ok(OuterEnum::Approved), + "delivered" => std::result::Result::Ok(OuterEnum::Delivered), + _ => std::result::Result::Err(format!("Value not valid: {}", s)), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct OuterNumber(f64); + +impl validator::Validate for OuterNumber { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for OuterNumber { + fn from(x: f64) -> Self { + OuterNumber(x) + } +} + +impl std::convert::From for f64 { + fn from(x: OuterNumber) -> Self { + x.0 + } +} + +impl std::ops::Deref for OuterNumber { + type Target = f64; + fn deref(&self) -> &f64 { + &self.0 + } +} + +impl std::ops::DerefMut for OuterNumber { + fn deref_mut(&mut self) -> &mut f64 { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct OuterString(String); + +impl validator::Validate for OuterString { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From for OuterString { + fn from(x: String) -> Self { + OuterString(x) + } +} + +impl std::string::ToString for OuterString { + fn to_string(&self) -> String { + self.0.to_string() + } +} + +impl std::str::FromStr for OuterString { + type Err = std::string::ParseError; + fn from_str(x: &str) -> std::result::Result { + std::result::Result::Ok(OuterString(x.to_string())) + } +} + +impl std::convert::From for String { + fn from(x: OuterString) -> Self { + x.0 + } +} + +impl std::ops::Deref for OuterString { + type Target = String; + fn deref(&self) -> &String { + &self.0 + } +} + +impl std::ops::DerefMut for OuterString { + fn deref_mut(&mut self) -> &mut String { + &mut self.0 + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Pet { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "category")] + #[serde(skip_serializing_if = "Option::is_none")] + pub category: Option, + + #[serde(rename = "name")] + pub name: String, + + #[serde(rename = "photoUrls")] + pub photo_urls: Vec, + + #[serde(rename = "tags")] + #[serde(skip_serializing_if = "Option::is_none")] + pub tags: Option>, + + /// pet status in the store + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, +} + +impl Pet { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(name: String, photo_urls: Vec) -> Pet { + Pet { + id: None, + category: None, + name, + photo_urls, + tags: None, + status: None, + } + } +} + +/// Converts the Pet value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Pet { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + // Skipping category in query parameter serialization + Some("name".to_string()), + Some(self.name.to_string()), + Some("photoUrls".to_string()), + Some( + self.photo_urls + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ), + // Skipping tags in query parameter serialization + self.status + .as_ref() + .map(|status| ["status".to_string(), status.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Pet value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Pet { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub category: Vec, + pub name: Vec, + pub photo_urls: Vec>, + pub tags: Vec>, + pub status: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Pet".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "category" => intermediate_rep.category.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + "photoUrls" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in Pet".to_string(), + ) + } + "tags" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in Pet".to_string(), + ) + } + #[allow(clippy::redundant_clone)] + "status" => intermediate_rep.status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Pet".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Pet { + id: intermediate_rep.id.into_iter().next(), + category: intermediate_rep.category.into_iter().next(), + name: intermediate_rep + .name + .into_iter() + .next() + .ok_or_else(|| "name missing in Pet".to_string())?, + photo_urls: intermediate_rep + .photo_urls + .into_iter() + .next() + .ok_or_else(|| "photoUrls missing in Pet".to_string())?, + tags: intermediate_rep.tags.into_iter().next(), + status: intermediate_rep.status.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Pet - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Pet - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ReadOnlyFirst { + #[serde(rename = "bar")] + #[serde(skip_serializing_if = "Option::is_none")] + pub bar: Option, + + #[serde(rename = "baz")] + #[serde(skip_serializing_if = "Option::is_none")] + pub baz: Option, +} + +impl ReadOnlyFirst { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ReadOnlyFirst { + ReadOnlyFirst { + bar: None, + baz: None, + } + } +} + +/// Converts the ReadOnlyFirst value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ReadOnlyFirst { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.bar + .as_ref() + .map(|bar| ["bar".to_string(), bar.to_string()].join(",")), + self.baz + .as_ref() + .map(|baz| ["baz".to_string(), baz.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ReadOnlyFirst value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ReadOnlyFirst { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub bar: Vec, + pub baz: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ReadOnlyFirst".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "bar" => intermediate_rep.bar.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "baz" => intermediate_rep.baz.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ReadOnlyFirst".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ReadOnlyFirst { + bar: intermediate_rep.bar.into_iter().next(), + baz: intermediate_rep.baz.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ReadOnlyFirst - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ReadOnlyFirst - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// Model for testing reserved words + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Return { + #[serde(rename = "return")] + #[serde(skip_serializing_if = "Option::is_none")] + pub r#return: Option, +} + +impl Return { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Return { + Return { r#return: None } + } +} + +/// Converts the Return value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Return { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .r#return + .as_ref() + .map(|r#return| ["return".to_string(), r#return.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Return value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Return { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub r#return: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Return".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "return" => intermediate_rep.r#return.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Return".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Return { + r#return: intermediate_rep.r#return.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Return - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Return - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Tag { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +impl Tag { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Tag { + Tag { + id: None, + name: None, + } + } +} + +/// Converts the Tag value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Tag { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Tag value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Tag { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub name: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Tag".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Tag".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Tag { + id: intermediate_rep.id.into_iter().next(), + name: intermediate_rep.name.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Tag - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Tag - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct User { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "username")] + #[serde(skip_serializing_if = "Option::is_none")] + pub username: Option, + + #[serde(rename = "firstName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub first_name: Option, + + #[serde(rename = "lastName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub last_name: Option, + + #[serde(rename = "email")] + #[serde(skip_serializing_if = "Option::is_none")] + pub email: Option, + + #[serde(rename = "password")] + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option, + + #[serde(rename = "phone")] + #[serde(skip_serializing_if = "Option::is_none")] + pub phone: Option, + + /// User Status + #[serde(rename = "userStatus")] + #[serde(skip_serializing_if = "Option::is_none")] + pub user_status: Option, +} + +impl User { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> User { + User { + id: None, + username: None, + first_name: None, + last_name: None, + email: None, + password: None, + phone: None, + user_status: None, + } + } +} + +/// Converts the User value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for User { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.username + .as_ref() + .map(|username| ["username".to_string(), username.to_string()].join(",")), + self.first_name + .as_ref() + .map(|first_name| ["firstName".to_string(), first_name.to_string()].join(",")), + self.last_name + .as_ref() + .map(|last_name| ["lastName".to_string(), last_name.to_string()].join(",")), + self.email + .as_ref() + .map(|email| ["email".to_string(), email.to_string()].join(",")), + self.password + .as_ref() + .map(|password| ["password".to_string(), password.to_string()].join(",")), + self.phone + .as_ref() + .map(|phone| ["phone".to_string(), phone.to_string()].join(",")), + self.user_status + .as_ref() + .map(|user_status| ["userStatus".to_string(), user_status.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a User value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for User { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub username: Vec, + pub first_name: Vec, + pub last_name: Vec, + pub email: Vec, + pub password: Vec, + pub phone: Vec, + pub user_status: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing User".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "username" => intermediate_rep.username.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "firstName" => intermediate_rep.first_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "lastName" => intermediate_rep.last_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "email" => intermediate_rep.email.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "password" => intermediate_rep.password.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "phone" => intermediate_rep.phone.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "userStatus" => intermediate_rep.user_status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing User".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(User { + id: intermediate_rep.id.into_iter().next(), + username: intermediate_rep.username.into_iter().next(), + first_name: intermediate_rep.first_name.into_iter().next(), + last_name: intermediate_rep.last_name.into_iter().next(), + email: intermediate_rep.email.into_iter().next(), + password: intermediate_rep.password.into_iter().next(), + phone: intermediate_rep.phone.into_iter().next(), + user_status: intermediate_rep.user_status.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for User - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into User - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs new file mode 100644 index 00000000000..673a8904e78 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs @@ -0,0 +1,2821 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{ + AddPetResponse, Api, Call123exampleResponse, CreateUserResponse, + CreateUsersWithArrayInputResponse, CreateUsersWithListInputResponse, DeleteOrderResponse, + DeletePetResponse, DeleteUserResponse, FakeOuterBooleanSerializeResponse, + FakeOuterCompositeSerializeResponse, FakeOuterNumberSerializeResponse, + FakeOuterStringSerializeResponse, FakeResponseWithNumericalDescriptionResponse, + FindPetsByStatusResponse, FindPetsByTagsResponse, GetInventoryResponse, GetOrderByIdResponse, + GetPetByIdResponse, GetUserByNameResponse, HyphenParamResponse, LoginUserResponse, + LogoutUserResponse, PlaceOrderResponse, TestBodyWithQueryParamsResponse, TestClassnameResponse, + TestClientModelResponse, TestEndpointParametersResponse, TestEnumParametersResponse, + TestInlineAdditionalPropertiesResponse, TestJsonFormDataResponse, TestSpecialTagsResponse, + UpdatePetResponse, UpdatePetWithFormResponse, UpdateUserResponse, UploadFileResponse, +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/v2/another-fake/dummy", patch(test_special_tags::)) + .route( + "/v2/fake", + get(test_enum_parameters::) + .patch(test_client_model::) + .post(test_endpoint_parameters::), + ) + .route( + "/v2/fake/body-with-query-params", + put(test_body_with_query_params::), + ) + .route( + "/v2/fake/hyphenParam/:hyphen_param", + get(hyphen_param::), + ) + .route( + "/v2/fake/inline-additionalProperties", + post(test_inline_additional_properties::), + ) + .route("/v2/fake/jsonFormData", get(test_json_form_data::)) + .route( + "/v2/fake/operation-with-numeric-id", + get(call123example::), + ) + .route( + "/v2/fake/outer/boolean", + post(fake_outer_boolean_serialize::), + ) + .route( + "/v2/fake/outer/composite", + post(fake_outer_composite_serialize::), + ) + .route( + "/v2/fake/outer/number", + post(fake_outer_number_serialize::), + ) + .route( + "/v2/fake/outer/string", + post(fake_outer_string_serialize::), + ) + .route( + "/v2/fake/response-with-numerical-description", + get(fake_response_with_numerical_description::), + ) + .route("/v2/fake_classname_test", patch(test_classname::)) + .route("/v2/pet", post(add_pet::).put(update_pet::)) + .route( + "/v2/pet/:pet_id", + delete(delete_pet::) + .get(get_pet_by_id::) + .post(update_pet_with_form::), + ) + .route("/v2/pet/:pet_id/uploadImage", post(upload_file::)) + .route("/v2/pet/findByStatus", get(find_pets_by_status::)) + .route("/v2/pet/findByTags", get(find_pets_by_tags::)) + .route("/v2/store/inventory", get(get_inventory::)) + .route("/v2/store/order", post(place_order::)) + .route( + "/v2/store/order/:order_id", + delete(delete_order::).get(get_order_by_id::), + ) + .route("/v2/user", post(create_user::)) + .route( + "/v2/user/:username", + delete(delete_user::) + .get(get_user_by_name::) + .put(update_user::), + ) + .route( + "/v2/user/createWithArray", + post(create_users_with_array_input::), + ) + .route( + "/v2/user/createWithList", + post(create_users_with_list_input::), + ) + .route("/v2/user/login", get(login_user::)) + .route("/v2/user/logout", get(logout_user::)) + .with_state(api_impl) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct TestSpecialTagsBodyValidator<'a> { + #[validate] + body: &'a models::Client, +} + +#[tracing::instrument(skip_all)] +fn test_special_tags_validation( + body: models::Client, +) -> std::result::Result<(models::Client,), ValidationErrors> { + let b = TestSpecialTagsBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// TestSpecialTags - PATCH /v2/another-fake/dummy +#[tracing::instrument(skip_all)] +async fn test_special_tags( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || test_special_tags_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_special_tags(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestSpecialTagsResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn call123example_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// Call123example - GET /v2/fake/operation-with-numeric-id +#[tracing::instrument(skip_all)] +async fn call123example( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || call123example_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .call123example(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + Call123exampleResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct FakeOuterBooleanSerializeBodyValidator<'a> { + #[validate] + body: &'a models::OuterBoolean, +} + +#[tracing::instrument(skip_all)] +fn fake_outer_boolean_serialize_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = FakeOuterBooleanSerializeBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} + +/// FakeOuterBooleanSerialize - POST /v2/fake/outer/boolean +#[tracing::instrument(skip_all)] +async fn fake_outer_boolean_serialize( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || fake_outer_boolean_serialize_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .fake_outer_boolean_serialize(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FakeOuterBooleanSerializeResponse::OutputBoolean(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct FakeOuterCompositeSerializeBodyValidator<'a> { + #[validate] + body: &'a models::OuterComposite, +} + +#[tracing::instrument(skip_all)] +fn fake_outer_composite_serialize_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = FakeOuterCompositeSerializeBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} + +/// FakeOuterCompositeSerialize - POST /v2/fake/outer/composite +#[tracing::instrument(skip_all)] +async fn fake_outer_composite_serialize( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || fake_outer_composite_serialize_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .fake_outer_composite_serialize(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FakeOuterCompositeSerializeResponse::OutputComposite(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct FakeOuterNumberSerializeBodyValidator<'a> { + #[validate] + body: &'a models::OuterNumber, +} + +#[tracing::instrument(skip_all)] +fn fake_outer_number_serialize_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = FakeOuterNumberSerializeBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} + +/// FakeOuterNumberSerialize - POST /v2/fake/outer/number +#[tracing::instrument(skip_all)] +async fn fake_outer_number_serialize( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || fake_outer_number_serialize_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .fake_outer_number_serialize(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FakeOuterNumberSerializeResponse::OutputNumber(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct FakeOuterStringSerializeBodyValidator<'a> { + #[validate] + body: &'a models::OuterString, +} + +#[tracing::instrument(skip_all)] +fn fake_outer_string_serialize_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = FakeOuterStringSerializeBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} + +/// FakeOuterStringSerialize - POST /v2/fake/outer/string +#[tracing::instrument(skip_all)] +async fn fake_outer_string_serialize( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || fake_outer_string_serialize_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .fake_outer_string_serialize(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FakeOuterStringSerializeResponse::OutputString(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn fake_response_with_numerical_description_validation() -> std::result::Result<(), ValidationErrors> +{ + Ok(()) +} + +/// FakeResponseWithNumericalDescription - GET /v2/fake/response-with-numerical-description +#[tracing::instrument(skip_all)] +async fn fake_response_with_numerical_description( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || fake_response_with_numerical_description_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .fake_response_with_numerical_description(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FakeResponseWithNumericalDescriptionResponse::Status200 => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn hyphen_param_validation( + path_params: models::HyphenParamPathParams, +) -> std::result::Result<(models::HyphenParamPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// HyphenParam - GET /v2/fake/hyphenParam/{hyphen-param} +#[tracing::instrument(skip_all)] +async fn hyphen_param( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || hyphen_param_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .hyphen_param(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + HyphenParamResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct TestBodyWithQueryParamsBodyValidator<'a> { + #[validate] + body: &'a models::User, +} + +#[tracing::instrument(skip_all)] +fn test_body_with_query_params_validation( + query_params: models::TestBodyWithQueryParamsQueryParams, + body: models::User, +) -> std::result::Result<(models::TestBodyWithQueryParamsQueryParams, models::User), ValidationErrors> +{ + query_params.validate()?; + let b = TestBodyWithQueryParamsBodyValidator { body: &body }; + b.validate()?; + + Ok((query_params, body)) +} + +/// TestBodyWithQueryParams - PUT /v2/fake/body-with-query-params +#[tracing::instrument(skip_all)] +async fn test_body_with_query_params( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || { + test_body_with_query_params_validation(query_params, body) + }) + .await + .unwrap(); + + let Ok((query_params, body)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_body_with_query_params(method, host, cookies, query_params, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestBodyWithQueryParamsResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct TestClientModelBodyValidator<'a> { + #[validate] + body: &'a models::Client, +} + +#[tracing::instrument(skip_all)] +fn test_client_model_validation( + body: models::Client, +) -> std::result::Result<(models::Client,), ValidationErrors> { + let b = TestClientModelBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// TestClientModel - PATCH /v2/fake +#[tracing::instrument(skip_all)] +async fn test_client_model( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || test_client_model_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_client_model(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestClientModelResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn test_endpoint_parameters_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// TestEndpointParameters - POST /v2/fake +#[tracing::instrument(skip_all)] +async fn test_endpoint_parameters( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || test_endpoint_parameters_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_endpoint_parameters(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestEndpointParametersResponse::InvalidUsernameSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + TestEndpointParametersResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn test_enum_parameters_validation( + header_params: models::TestEnumParametersHeaderParams, + query_params: models::TestEnumParametersQueryParams, +) -> std::result::Result< + ( + models::TestEnumParametersHeaderParams, + models::TestEnumParametersQueryParams, + ), + ValidationErrors, +> { + header_params.validate()?; + query_params.validate()?; + + Ok((header_params, query_params)) +} + +/// TestEnumParameters - GET /v2/fake +#[tracing::instrument(skip_all)] +async fn test_enum_parameters( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + // Header parameters + let header_params = { + let header_enum_header_string_array = + headers.get(HeaderName::from_static("enum_header_string_array")); + + let header_enum_header_string_array = match header_enum_header_string_array { + Some(v) => match header::IntoHeaderValue::>::try_from((*v).clone()) { + Ok(result) => Some(result.0), + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!( + "Invalid header enum_header_string_array - {}", + err + ))) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }, + None => None, + }; + let header_enum_header_string = headers.get(HeaderName::from_static("enum_header_string")); + + let header_enum_header_string = match header_enum_header_string { + Some(v) => match header::IntoHeaderValue::::try_from((*v).clone()) { + Ok(result) => Some(result.0), + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!( + "Invalid header enum_header_string - {}", + err + ))) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }, + None => None, + }; + + models::TestEnumParametersHeaderParams { + enum_header_string_array: header_enum_header_string_array, + enum_header_string: header_enum_header_string, + } + }; + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || { + test_enum_parameters_validation(header_params, query_params) + }) + .await + .unwrap(); + + let Ok((header_params, query_params)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_enum_parameters(method, host, cookies, header_params, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestEnumParametersResponse::InvalidRequest => { + let mut response = response.status(400); + response.body(Body::empty()) + } + TestEnumParametersResponse::NotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct TestInlineAdditionalPropertiesBodyValidator<'a> { + body: &'a std::collections::HashMap, +} + +#[tracing::instrument(skip_all)] +fn test_inline_additional_properties_validation( + body: std::collections::HashMap, +) -> std::result::Result<(std::collections::HashMap,), ValidationErrors> { + let b = TestInlineAdditionalPropertiesBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// TestInlineAdditionalProperties - POST /v2/fake/inline-additionalProperties +#[tracing::instrument(skip_all)] +async fn test_inline_additional_properties( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || test_inline_additional_properties_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_inline_additional_properties(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestInlineAdditionalPropertiesResponse::SuccessfulOperation => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn test_json_form_data_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// TestJsonFormData - GET /v2/fake/jsonFormData +#[tracing::instrument(skip_all)] +async fn test_json_form_data( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || test_json_form_data_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_json_form_data(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestJsonFormDataResponse::SuccessfulOperation => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct TestClassnameBodyValidator<'a> { + #[validate] + body: &'a models::Client, +} + +#[tracing::instrument(skip_all)] +fn test_classname_validation( + body: models::Client, +) -> std::result::Result<(models::Client,), ValidationErrors> { + let b = TestClassnameBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// TestClassname - PATCH /v2/fake_classname_test +#[tracing::instrument(skip_all)] +async fn test_classname( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || test_classname_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .test_classname(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + TestClassnameResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct AddPetBodyValidator<'a> { + #[validate] + body: &'a models::Pet, +} + +#[tracing::instrument(skip_all)] +fn add_pet_validation(body: models::Pet) -> std::result::Result<(models::Pet,), ValidationErrors> { + let b = AddPetBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// AddPet - POST /v2/pet +#[tracing::instrument(skip_all)] +async fn add_pet( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || add_pet_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().add_pet(method, host, cookies, body).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + AddPetResponse::InvalidInput => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_pet_validation( + header_params: models::DeletePetHeaderParams, + path_params: models::DeletePetPathParams, +) -> std::result::Result< + (models::DeletePetHeaderParams, models::DeletePetPathParams), + ValidationErrors, +> { + header_params.validate()?; + path_params.validate()?; + + Ok((header_params, path_params)) +} + +/// DeletePet - DELETE /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn delete_pet( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + // Header parameters + let header_params = { + let header_api_key = headers.get(HeaderName::from_static("api_key")); + + let header_api_key = match header_api_key { + Some(v) => match header::IntoHeaderValue::::try_from((*v).clone()) { + Ok(result) => Some(result.0), + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Invalid header api_key - {}", err))) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }, + None => None, + }; + + models::DeletePetHeaderParams { + api_key: header_api_key, + } + }; + + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || delete_pet_validation(header_params, path_params)) + .await + .unwrap(); + + let Ok((header_params, path_params)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_pet(method, host, cookies, header_params, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeletePetResponse::InvalidPetValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn find_pets_by_status_validation( + query_params: models::FindPetsByStatusQueryParams, +) -> std::result::Result<(models::FindPetsByStatusQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// FindPetsByStatus - GET /v2/pet/findByStatus +#[tracing::instrument(skip_all)] +async fn find_pets_by_status( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || find_pets_by_status_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .find_pets_by_status(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FindPetsByStatusResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + FindPetsByStatusResponse::InvalidStatusValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn find_pets_by_tags_validation( + query_params: models::FindPetsByTagsQueryParams, +) -> std::result::Result<(models::FindPetsByTagsQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// FindPetsByTags - GET /v2/pet/findByTags +#[tracing::instrument(skip_all)] +async fn find_pets_by_tags( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || find_pets_by_tags_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .find_pets_by_tags(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FindPetsByTagsResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + FindPetsByTagsResponse::InvalidTagValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_pet_by_id_validation( + path_params: models::GetPetByIdPathParams, +) -> std::result::Result<(models::GetPetByIdPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetPetById - GET /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn get_pet_by_id( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_pet_by_id_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_pet_by_id(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetPetByIdResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetPetByIdResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetPetByIdResponse::PetNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct UpdatePetBodyValidator<'a> { + #[validate] + body: &'a models::Pet, +} + +#[tracing::instrument(skip_all)] +fn update_pet_validation( + body: models::Pet, +) -> std::result::Result<(models::Pet,), ValidationErrors> { + let b = UpdatePetBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// UpdatePet - PUT /v2/pet +#[tracing::instrument(skip_all)] +async fn update_pet( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || update_pet_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_pet(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdatePetResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + UpdatePetResponse::PetNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + UpdatePetResponse::ValidationException => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn update_pet_with_form_validation( + path_params: models::UpdatePetWithFormPathParams, +) -> std::result::Result<(models::UpdatePetWithFormPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// UpdatePetWithForm - POST /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn update_pet_with_form( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || update_pet_with_form_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_pet_with_form(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdatePetWithFormResponse::InvalidInput => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn upload_file_validation( + path_params: models::UploadFilePathParams, +) -> std::result::Result<(models::UploadFilePathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// UploadFile - POST /v2/pet/{petId}/uploadImage +#[tracing::instrument(skip_all)] +async fn upload_file( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, + body: Multipart, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || upload_file_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .upload_file(method, host, cookies, path_params, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UploadFileResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_order_validation( + path_params: models::DeleteOrderPathParams, +) -> std::result::Result<(models::DeleteOrderPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// DeleteOrder - DELETE /v2/store/order/{order_id} +#[tracing::instrument(skip_all)] +async fn delete_order( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || delete_order_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_order(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeleteOrderResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + DeleteOrderResponse::OrderNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_inventory_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// GetInventory - GET /v2/store/inventory +#[tracing::instrument(skip_all)] +async fn get_inventory( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_inventory_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().get_inventory(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetInventoryResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_order_by_id_validation( + path_params: models::GetOrderByIdPathParams, +) -> std::result::Result<(models::GetOrderByIdPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetOrderById - GET /v2/store/order/{order_id} +#[tracing::instrument(skip_all)] +async fn get_order_by_id( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_order_by_id_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_order_by_id(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetOrderByIdResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetOrderByIdResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetOrderByIdResponse::OrderNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct PlaceOrderBodyValidator<'a> { + #[validate] + body: &'a models::Order, +} + +#[tracing::instrument(skip_all)] +fn place_order_validation( + body: models::Order, +) -> std::result::Result<(models::Order,), ValidationErrors> { + let b = PlaceOrderBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// PlaceOrder - POST /v2/store/order +#[tracing::instrument(skip_all)] +async fn place_order( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || place_order_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .place_order(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + PlaceOrderResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + PlaceOrderResponse::InvalidOrder => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUserBodyValidator<'a> { + #[validate] + body: &'a models::User, +} + +#[tracing::instrument(skip_all)] +fn create_user_validation( + body: models::User, +) -> std::result::Result<(models::User,), ValidationErrors> { + let b = CreateUserBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUser - POST /v2/user +#[tracing::instrument(skip_all)] +async fn create_user( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || create_user_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_user(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUserResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUsersWithArrayInputBodyValidator<'a> { + #[validate] + body: &'a Vec, +} + +#[tracing::instrument(skip_all)] +fn create_users_with_array_input_validation( + body: Vec, +) -> std::result::Result<(Vec,), ValidationErrors> { + let b = CreateUsersWithArrayInputBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUsersWithArrayInput - POST /v2/user/createWithArray +#[tracing::instrument(skip_all)] +async fn create_users_with_array_input( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || create_users_with_array_input_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_users_with_array_input(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUsersWithArrayInputResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUsersWithListInputBodyValidator<'a> { + #[validate] + body: &'a Vec, +} + +#[tracing::instrument(skip_all)] +fn create_users_with_list_input_validation( + body: Vec, +) -> std::result::Result<(Vec,), ValidationErrors> { + let b = CreateUsersWithListInputBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUsersWithListInput - POST /v2/user/createWithList +#[tracing::instrument(skip_all)] +async fn create_users_with_list_input( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || create_users_with_list_input_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_users_with_list_input(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUsersWithListInputResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_user_validation( + path_params: models::DeleteUserPathParams, +) -> std::result::Result<(models::DeleteUserPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// DeleteUser - DELETE /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn delete_user( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || delete_user_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_user(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeleteUserResponse::InvalidUsernameSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + DeleteUserResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_user_by_name_validation( + path_params: models::GetUserByNamePathParams, +) -> std::result::Result<(models::GetUserByNamePathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetUserByName - GET /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn get_user_by_name( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_user_by_name_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_user_by_name(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetUserByNameResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetUserByNameResponse::InvalidUsernameSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetUserByNameResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn login_user_validation( + query_params: models::LoginUserQueryParams, +) -> std::result::Result<(models::LoginUserQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// LoginUser - GET /v2/user/login +#[tracing::instrument(skip_all)] +async fn login_user( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || login_user_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .login_user(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + LoginUserResponse::SuccessfulOperation { + body, + x_rate_limit, + x_expires_after, + } => { + if let Some(x_rate_limit) = x_rate_limit { + let x_rate_limit = match header::IntoHeaderValue(x_rate_limit).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling x_rate_limit header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("x-rate-limit"), x_rate_limit); + } + } + if let Some(x_expires_after) = x_expires_after { + let x_expires_after = match header::IntoHeaderValue(x_expires_after).try_into() + { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling x_expires_after header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("x-expires-after"), x_expires_after); + } + } + + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + LoginUserResponse::InvalidUsername => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn logout_user_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// LogoutUser - GET /v2/user/logout +#[tracing::instrument(skip_all)] +async fn logout_user( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || logout_user_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().logout_user(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + LogoutUserResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct UpdateUserBodyValidator<'a> { + #[validate] + body: &'a models::User, +} + +#[tracing::instrument(skip_all)] +fn update_user_validation( + path_params: models::UpdateUserPathParams, + body: models::User, +) -> std::result::Result<(models::UpdateUserPathParams, models::User), ValidationErrors> { + path_params.validate()?; + let b = UpdateUserBodyValidator { body: &body }; + b.validate()?; + + Ok((path_params, body)) +} + +/// UpdateUser - PUT /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn update_user( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || update_user_validation(path_params, body)) + .await + .unwrap(); + + let Ok((path_params, body)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_user(method, host, cookies, path_params, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdateUserResponse::InvalidUserSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + UpdateUserResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/types.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/types.rs new file mode 100644 index 00000000000..c17c655d720 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use petstore_with_fake_endpoints_models_for_testing::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore/.gitignore b/samples/server/petstore/rust-axum/output/petstore/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/petstore/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/petstore/Cargo.toml b/samples/server/petstore/rust-axum/output/petstore/Cargo.toml new file mode 100644 index 00000000000..bdc5cf7766c --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "petstore" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters." +license = "Apache-2.0" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/petstore/README.md b/samples/server/petstore/rust-axum/output/petstore/README.md new file mode 100644 index 00000000000..3a9a839d794 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/README.md @@ -0,0 +1,91 @@ +# Rust API for petstore + +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0.0 + + + + +This autogenerated project defines an API crate `petstore` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl petstore::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = petstore::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/petstore/src/header.rs b/samples/server/petstore/rust-axum/output/petstore/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore/src/lib.rs b/samples/server/petstore/rust-axum/output/petstore/src/lib.rs new file mode 100644 index 00000000000..b49a7755d1a --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/src/lib.rs @@ -0,0 +1,441 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = "/v2"; +pub const API_VERSION: &str = "1.0.0"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum AddPetResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid input + InvalidInput, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum DeletePetResponse { + /// Invalid pet value + InvalidPetValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum FindPetsByStatusResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid status value + InvalidStatusValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum FindPetsByTagsResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid tag value + InvalidTagValue, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetPetByIdResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid ID supplied + InvalidIDSupplied, + /// Pet not found + PetNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum UpdatePetResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid ID supplied + InvalidIDSupplied, + /// Pet not found + PetNotFound, + /// Validation exception + ValidationException, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UpdatePetWithFormResponse { + /// Invalid input + InvalidInput, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum UploadFileResponse { + /// successful operation + SuccessfulOperation(models::ApiResponse), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum DeleteOrderResponse { + /// Invalid ID supplied + InvalidIDSupplied, + /// Order not found + OrderNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GetInventoryResponse { + /// successful operation + SuccessfulOperation(std::collections::HashMap), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetOrderByIdResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid ID supplied + InvalidIDSupplied, + /// Order not found + OrderNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum PlaceOrderResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid Order + InvalidOrder, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUserResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUsersWithArrayInputResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum CreateUsersWithListInputResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum DeleteUserResponse { + /// Invalid username supplied + InvalidUsernameSupplied, + /// User not found + UserNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetUserByNameResponse { + /// successful operation + SuccessfulOperation(String), + /// Invalid username supplied + InvalidUsernameSupplied, + /// User not found + UserNotFound, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum LoginUserResponse { + /// successful operation + SuccessfulOperation { + body: String, + set_cookie: Option, + x_rate_limit: Option, + x_expires_after: Option>, + }, + /// Invalid username/password supplied + InvalidUsername, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum LogoutUserResponse { + /// successful operation + SuccessfulOperation, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum UpdateUserResponse { + /// Invalid user supplied + InvalidUserSupplied, + /// User not found + UserNotFound, +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// Add a new pet to the store. + /// + /// AddPet - POST /v2/pet + async fn add_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Pet, + ) -> Result; + + /// Deletes a pet. + /// + /// DeletePet - DELETE /v2/pet/{petId} + async fn delete_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + header_params: models::DeletePetHeaderParams, + path_params: models::DeletePetPathParams, + ) -> Result; + + /// Finds Pets by status. + /// + /// FindPetsByStatus - GET /v2/pet/findByStatus + async fn find_pets_by_status( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::FindPetsByStatusQueryParams, + ) -> Result; + + /// Finds Pets by tags. + /// + /// FindPetsByTags - GET /v2/pet/findByTags + async fn find_pets_by_tags( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::FindPetsByTagsQueryParams, + ) -> Result; + + /// Find pet by ID. + /// + /// GetPetById - GET /v2/pet/{petId} + async fn get_pet_by_id( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetPetByIdPathParams, + ) -> Result; + + /// Update an existing pet. + /// + /// UpdatePet - PUT /v2/pet + async fn update_pet( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Pet, + ) -> Result; + + /// Updates a pet in the store with form data. + /// + /// UpdatePetWithForm - POST /v2/pet/{petId} + async fn update_pet_with_form( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UpdatePetWithFormPathParams, + ) -> Result; + + /// uploads an image. + /// + /// UploadFile - POST /v2/pet/{petId}/uploadImage + async fn upload_file( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UploadFilePathParams, + body: Multipart, + ) -> Result; + + /// Delete purchase order by ID. + /// + /// DeleteOrder - DELETE /v2/store/order/{orderId} + async fn delete_order( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::DeleteOrderPathParams, + ) -> Result; + + /// Returns pet inventories by status. + /// + /// GetInventory - GET /v2/store/inventory + async fn get_inventory( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Find purchase order by ID. + /// + /// GetOrderById - GET /v2/store/order/{orderId} + async fn get_order_by_id( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetOrderByIdPathParams, + ) -> Result; + + /// Place an order for a pet. + /// + /// PlaceOrder - POST /v2/store/order + async fn place_order( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::Order, + ) -> Result; + + /// Create user. + /// + /// CreateUser - POST /v2/user + async fn create_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::User, + ) -> Result; + + /// Creates list of users with given input array. + /// + /// CreateUsersWithArrayInput - POST /v2/user/createWithArray + async fn create_users_with_array_input( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Vec, + ) -> Result; + + /// Creates list of users with given input array. + /// + /// CreateUsersWithListInput - POST /v2/user/createWithList + async fn create_users_with_list_input( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: Vec, + ) -> Result; + + /// Delete user. + /// + /// DeleteUser - DELETE /v2/user/{username} + async fn delete_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::DeleteUserPathParams, + ) -> Result; + + /// Get user by user name. + /// + /// GetUserByName - GET /v2/user/{username} + async fn get_user_by_name( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GetUserByNamePathParams, + ) -> Result; + + /// Logs user into the system. + /// + /// LoginUser - GET /v2/user/login + async fn login_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::LoginUserQueryParams, + ) -> Result; + + /// Logs out current logged in user session. + /// + /// LogoutUser - GET /v2/user/logout + async fn logout_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Updated user. + /// + /// UpdateUser - PUT /v2/user/{username} + async fn update_user( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::UpdateUserPathParams, + body: models::User, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/petstore/src/models.rs b/samples/server/petstore/rust-axum/output/petstore/src/models.rs new file mode 100644 index 00000000000..2ad59347109 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/src/models.rs @@ -0,0 +1,1215 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeletePetHeaderParams { + pub api_key: Option, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeletePetPathParams { + /// Pet id to delete + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct FindPetsByStatusQueryParams { + /// Status values that need to be considered for filter + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + pub status: Vec, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct FindPetsByTagsQueryParams { + /// Tags to filter by + #[serde(rename = "tags")] + pub tags: Vec, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetPetByIdPathParams { + /// ID of pet to return + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UpdatePetWithFormPathParams { + /// ID of pet that needs to be updated + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UploadFilePathParams { + /// ID of pet to update + pub pet_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeleteOrderPathParams { + /// ID of the order that needs to be deleted + pub order_id: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetOrderByIdPathParams { + /// ID of pet that needs to be fetched + #[validate(range(min = 1, max = 5))] + pub order_id: i64, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DeleteUserPathParams { + /// The name that needs to be deleted + pub username: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetUserByNamePathParams { + /// The name that needs to be fetched. Use user1 for testing. + pub username: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct LoginUserQueryParams { + /// The user name for login + #[serde(rename = "username")] + #[validate(regex = "RE_LOGINUSERQUERYPARAMS_USERNAME")] + pub username: String, + /// The password for login in clear text + #[serde(rename = "password")] + pub password: String, +} + +lazy_static::lazy_static! { + static ref RE_LOGINUSERQUERYPARAMS_USERNAME: regex::Regex = regex::Regex::new(r"^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$").unwrap(); +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct UpdateUserPathParams { + /// name that need to be deleted + pub username: String, +} + +/// Describes the result of uploading an image resource + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ApiResponse { + #[serde(rename = "code")] + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option, + + #[serde(rename = "type")] + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, + + #[serde(rename = "message")] + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +impl ApiResponse { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ApiResponse { + ApiResponse { + code: None, + r#type: None, + message: None, + } + } +} + +/// Converts the ApiResponse value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ApiResponse { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.code + .as_ref() + .map(|code| ["code".to_string(), code.to_string()].join(",")), + self.r#type + .as_ref() + .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")), + self.message + .as_ref() + .map(|message| ["message".to_string(), message.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ApiResponse value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ApiResponse { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub code: Vec, + pub r#type: Vec, + pub message: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ApiResponse".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "code" => intermediate_rep.code.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "message" => intermediate_rep.message.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ApiResponse".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ApiResponse { + code: intermediate_rep.code.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + message: intermediate_rep.message.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ApiResponse - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ApiResponse - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// A category for a pet + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Category { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "name")] + #[validate(regex = "RE_CATEGORY_NAME")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +lazy_static::lazy_static! { + static ref RE_CATEGORY_NAME: regex::Regex = regex::Regex::new(r"^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$").unwrap(); +} + +impl Category { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Category { + Category { + id: None, + name: None, + } + } +} + +/// Converts the Category value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Category { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Category value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Category { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub name: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Category".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Category".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Category { + id: intermediate_rep.id.into_iter().next(), + name: intermediate_rep.name.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Category - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Category - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// An order for a pets from the pet store + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Order { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "petId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub pet_id: Option, + + #[serde(rename = "quantity")] + #[serde(skip_serializing_if = "Option::is_none")] + pub quantity: Option, + + #[serde(rename = "shipDate")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ship_date: Option>, + + /// Order Status + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + + #[serde(rename = "complete")] + #[serde(skip_serializing_if = "Option::is_none")] + pub complete: Option, +} + +impl Order { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Order { + Order { + id: None, + pet_id: None, + quantity: None, + ship_date: None, + status: None, + complete: Some(false), + } + } +} + +/// Converts the Order value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Order { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.pet_id + .as_ref() + .map(|pet_id| ["petId".to_string(), pet_id.to_string()].join(",")), + self.quantity + .as_ref() + .map(|quantity| ["quantity".to_string(), quantity.to_string()].join(",")), + // Skipping shipDate in query parameter serialization + self.status + .as_ref() + .map(|status| ["status".to_string(), status.to_string()].join(",")), + self.complete + .as_ref() + .map(|complete| ["complete".to_string(), complete.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Order value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Order { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub pet_id: Vec, + pub quantity: Vec, + pub ship_date: Vec>, + pub status: Vec, + pub complete: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Order".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "petId" => intermediate_rep.pet_id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "quantity" => intermediate_rep.quantity.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "shipDate" => intermediate_rep.ship_date.push( + as std::str::FromStr>::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "status" => intermediate_rep.status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "complete" => intermediate_rep.complete.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Order".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Order { + id: intermediate_rep.id.into_iter().next(), + pet_id: intermediate_rep.pet_id.into_iter().next(), + quantity: intermediate_rep.quantity.into_iter().next(), + ship_date: intermediate_rep.ship_date.into_iter().next(), + status: intermediate_rep.status.into_iter().next(), + complete: intermediate_rep.complete.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Order - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Order - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// A pet for sale in the pet store + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Pet { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "category")] + #[serde(skip_serializing_if = "Option::is_none")] + pub category: Option, + + #[serde(rename = "name")] + pub name: String, + + #[serde(rename = "photoUrls")] + pub photo_urls: Vec, + + #[serde(rename = "tags")] + #[serde(skip_serializing_if = "Option::is_none")] + pub tags: Option>, + + /// pet status in the store + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "status")] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, +} + +impl Pet { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(name: String, photo_urls: Vec) -> Pet { + Pet { + id: None, + category: None, + name, + photo_urls, + tags: None, + status: None, + } + } +} + +/// Converts the Pet value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Pet { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + // Skipping category in query parameter serialization + Some("name".to_string()), + Some(self.name.to_string()), + Some("photoUrls".to_string()), + Some( + self.photo_urls + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ), + // Skipping tags in query parameter serialization + self.status + .as_ref() + .map(|status| ["status".to_string(), status.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Pet value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Pet { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub category: Vec, + pub name: Vec, + pub photo_urls: Vec>, + pub tags: Vec>, + pub status: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Pet".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "category" => intermediate_rep.category.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + "photoUrls" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in Pet".to_string(), + ) + } + "tags" => { + return std::result::Result::Err( + "Parsing a container in this style is not supported in Pet".to_string(), + ) + } + #[allow(clippy::redundant_clone)] + "status" => intermediate_rep.status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Pet".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Pet { + id: intermediate_rep.id.into_iter().next(), + category: intermediate_rep.category.into_iter().next(), + name: intermediate_rep + .name + .into_iter() + .next() + .ok_or_else(|| "name missing in Pet".to_string())?, + photo_urls: intermediate_rep + .photo_urls + .into_iter() + .next() + .ok_or_else(|| "photoUrls missing in Pet".to_string())?, + tags: intermediate_rep.tags.into_iter().next(), + status: intermediate_rep.status.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Pet - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Pet - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// A tag for a pet + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Tag { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +impl Tag { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Tag { + Tag { + id: None, + name: None, + } + } +} + +/// Converts the Tag value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Tag { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Tag value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Tag { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub name: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Tag".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Tag".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Tag { + id: intermediate_rep.id.into_iter().next(), + name: intermediate_rep.name.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Tag - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Tag - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// A User who is purchasing from the pet store + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct User { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "username")] + #[serde(skip_serializing_if = "Option::is_none")] + pub username: Option, + + #[serde(rename = "firstName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub first_name: Option, + + #[serde(rename = "lastName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub last_name: Option, + + #[serde(rename = "email")] + #[serde(skip_serializing_if = "Option::is_none")] + pub email: Option, + + #[serde(rename = "password")] + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option, + + #[serde(rename = "phone")] + #[serde(skip_serializing_if = "Option::is_none")] + pub phone: Option, + + /// User Status + #[serde(rename = "userStatus")] + #[serde(skip_serializing_if = "Option::is_none")] + pub user_status: Option, +} + +impl User { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> User { + User { + id: None, + username: None, + first_name: None, + last_name: None, + email: None, + password: None, + phone: None, + user_status: None, + } + } +} + +/// Converts the User value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for User { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.id + .as_ref() + .map(|id| ["id".to_string(), id.to_string()].join(",")), + self.username + .as_ref() + .map(|username| ["username".to_string(), username.to_string()].join(",")), + self.first_name + .as_ref() + .map(|first_name| ["firstName".to_string(), first_name.to_string()].join(",")), + self.last_name + .as_ref() + .map(|last_name| ["lastName".to_string(), last_name.to_string()].join(",")), + self.email + .as_ref() + .map(|email| ["email".to_string(), email.to_string()].join(",")), + self.password + .as_ref() + .map(|password| ["password".to_string(), password.to_string()].join(",")), + self.phone + .as_ref() + .map(|phone| ["phone".to_string(), phone.to_string()].join(",")), + self.user_status + .as_ref() + .map(|user_status| ["userStatus".to_string(), user_status.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a User value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for User { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub username: Vec, + pub first_name: Vec, + pub last_name: Vec, + pub email: Vec, + pub password: Vec, + pub phone: Vec, + pub user_status: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing User".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "username" => intermediate_rep.username.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "firstName" => intermediate_rep.first_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "lastName" => intermediate_rep.last_name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "email" => intermediate_rep.email.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "password" => intermediate_rep.password.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "phone" => intermediate_rep.phone.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "userStatus" => intermediate_rep.user_status.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing User".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(User { + id: intermediate_rep.id.into_iter().next(), + username: intermediate_rep.username.into_iter().next(), + first_name: intermediate_rep.first_name.into_iter().next(), + last_name: intermediate_rep.last_name.into_iter().next(), + email: intermediate_rep.email.into_iter().next(), + password: intermediate_rep.password.into_iter().next(), + phone: intermediate_rep.phone.into_iter().next(), + user_status: intermediate_rep.user_status.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for User - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into User - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs b/samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs new file mode 100644 index 00000000000..4ce029a66fa --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs @@ -0,0 +1,1630 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{ + AddPetResponse, Api, CreateUserResponse, CreateUsersWithArrayInputResponse, + CreateUsersWithListInputResponse, DeleteOrderResponse, DeletePetResponse, DeleteUserResponse, + FindPetsByStatusResponse, FindPetsByTagsResponse, GetInventoryResponse, GetOrderByIdResponse, + GetPetByIdResponse, GetUserByNameResponse, LoginUserResponse, LogoutUserResponse, + PlaceOrderResponse, UpdatePetResponse, UpdatePetWithFormResponse, UpdateUserResponse, + UploadFileResponse, +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/v2/pet", post(add_pet::).put(update_pet::)) + .route( + "/v2/pet/:pet_id", + delete(delete_pet::) + .get(get_pet_by_id::) + .post(update_pet_with_form::), + ) + .route("/v2/pet/:pet_id/uploadImage", post(upload_file::)) + .route("/v2/pet/findByStatus", get(find_pets_by_status::)) + .route("/v2/pet/findByTags", get(find_pets_by_tags::)) + .route("/v2/store/inventory", get(get_inventory::)) + .route("/v2/store/order", post(place_order::)) + .route( + "/v2/store/order/:order_id", + delete(delete_order::).get(get_order_by_id::), + ) + .route("/v2/user", post(create_user::)) + .route( + "/v2/user/:username", + delete(delete_user::) + .get(get_user_by_name::) + .put(update_user::), + ) + .route( + "/v2/user/createWithArray", + post(create_users_with_array_input::), + ) + .route( + "/v2/user/createWithList", + post(create_users_with_list_input::), + ) + .route("/v2/user/login", get(login_user::)) + .route("/v2/user/logout", get(logout_user::)) + .with_state(api_impl) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct AddPetBodyValidator<'a> { + #[validate] + body: &'a models::Pet, +} + +#[tracing::instrument(skip_all)] +fn add_pet_validation(body: models::Pet) -> std::result::Result<(models::Pet,), ValidationErrors> { + let b = AddPetBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// AddPet - POST /v2/pet +#[tracing::instrument(skip_all)] +async fn add_pet( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || add_pet_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().add_pet(method, host, cookies, body).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + AddPetResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + AddPetResponse::InvalidInput => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_pet_validation( + header_params: models::DeletePetHeaderParams, + path_params: models::DeletePetPathParams, +) -> std::result::Result< + (models::DeletePetHeaderParams, models::DeletePetPathParams), + ValidationErrors, +> { + header_params.validate()?; + path_params.validate()?; + + Ok((header_params, path_params)) +} + +/// DeletePet - DELETE /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn delete_pet( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + // Header parameters + let header_params = { + let header_api_key = headers.get(HeaderName::from_static("api_key")); + + let header_api_key = match header_api_key { + Some(v) => match header::IntoHeaderValue::::try_from((*v).clone()) { + Ok(result) => Some(result.0), + Err(err) => { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Invalid header api_key - {}", err))) + .map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }); + } + }, + None => None, + }; + + models::DeletePetHeaderParams { + api_key: header_api_key, + } + }; + + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || delete_pet_validation(header_params, path_params)) + .await + .unwrap(); + + let Ok((header_params, path_params)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_pet(method, host, cookies, header_params, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeletePetResponse::InvalidPetValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn find_pets_by_status_validation( + query_params: models::FindPetsByStatusQueryParams, +) -> std::result::Result<(models::FindPetsByStatusQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// FindPetsByStatus - GET /v2/pet/findByStatus +#[tracing::instrument(skip_all)] +async fn find_pets_by_status( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || find_pets_by_status_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .find_pets_by_status(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FindPetsByStatusResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + FindPetsByStatusResponse::InvalidStatusValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn find_pets_by_tags_validation( + query_params: models::FindPetsByTagsQueryParams, +) -> std::result::Result<(models::FindPetsByTagsQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// FindPetsByTags - GET /v2/pet/findByTags +#[tracing::instrument(skip_all)] +async fn find_pets_by_tags( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || find_pets_by_tags_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .find_pets_by_tags(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FindPetsByTagsResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + FindPetsByTagsResponse::InvalidTagValue => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_pet_by_id_validation( + path_params: models::GetPetByIdPathParams, +) -> std::result::Result<(models::GetPetByIdPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetPetById - GET /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn get_pet_by_id( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_pet_by_id_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_pet_by_id(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetPetByIdResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetPetByIdResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetPetByIdResponse::PetNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct UpdatePetBodyValidator<'a> { + #[validate] + body: &'a models::Pet, +} + +#[tracing::instrument(skip_all)] +fn update_pet_validation( + body: models::Pet, +) -> std::result::Result<(models::Pet,), ValidationErrors> { + let b = UpdatePetBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// UpdatePet - PUT /v2/pet +#[tracing::instrument(skip_all)] +async fn update_pet( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || update_pet_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_pet(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdatePetResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + UpdatePetResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + UpdatePetResponse::PetNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + UpdatePetResponse::ValidationException => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn update_pet_with_form_validation( + path_params: models::UpdatePetWithFormPathParams, +) -> std::result::Result<(models::UpdatePetWithFormPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// UpdatePetWithForm - POST /v2/pet/{petId} +#[tracing::instrument(skip_all)] +async fn update_pet_with_form( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || update_pet_with_form_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_pet_with_form(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdatePetWithFormResponse::InvalidInput => { + let mut response = response.status(405); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn upload_file_validation( + path_params: models::UploadFilePathParams, +) -> std::result::Result<(models::UploadFilePathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// UploadFile - POST /v2/pet/{petId}/uploadImage +#[tracing::instrument(skip_all)] +async fn upload_file( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, + body: Multipart, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || upload_file_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .upload_file(method, host, cookies, path_params, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UploadFileResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_order_validation( + path_params: models::DeleteOrderPathParams, +) -> std::result::Result<(models::DeleteOrderPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// DeleteOrder - DELETE /v2/store/order/{orderId} +#[tracing::instrument(skip_all)] +async fn delete_order( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || delete_order_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_order(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeleteOrderResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + DeleteOrderResponse::OrderNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_inventory_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// GetInventory - GET /v2/store/inventory +#[tracing::instrument(skip_all)] +async fn get_inventory( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_inventory_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().get_inventory(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetInventoryResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_order_by_id_validation( + path_params: models::GetOrderByIdPathParams, +) -> std::result::Result<(models::GetOrderByIdPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetOrderById - GET /v2/store/order/{orderId} +#[tracing::instrument(skip_all)] +async fn get_order_by_id( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_order_by_id_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_order_by_id(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetOrderByIdResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetOrderByIdResponse::InvalidIDSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetOrderByIdResponse::OrderNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct PlaceOrderBodyValidator<'a> { + #[validate] + body: &'a models::Order, +} + +#[tracing::instrument(skip_all)] +fn place_order_validation( + body: models::Order, +) -> std::result::Result<(models::Order,), ValidationErrors> { + let b = PlaceOrderBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// PlaceOrder - POST /v2/store/order +#[tracing::instrument(skip_all)] +async fn place_order( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || place_order_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .place_order(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + PlaceOrderResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + PlaceOrderResponse::InvalidOrder => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUserBodyValidator<'a> { + #[validate] + body: &'a models::User, +} + +#[tracing::instrument(skip_all)] +fn create_user_validation( + body: models::User, +) -> std::result::Result<(models::User,), ValidationErrors> { + let b = CreateUserBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUser - POST /v2/user +#[tracing::instrument(skip_all)] +async fn create_user( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || create_user_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_user(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUserResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUsersWithArrayInputBodyValidator<'a> { + #[validate] + body: &'a Vec, +} + +#[tracing::instrument(skip_all)] +fn create_users_with_array_input_validation( + body: Vec, +) -> std::result::Result<(Vec,), ValidationErrors> { + let b = CreateUsersWithArrayInputBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUsersWithArrayInput - POST /v2/user/createWithArray +#[tracing::instrument(skip_all)] +async fn create_users_with_array_input( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || create_users_with_array_input_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_users_with_array_input(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUsersWithArrayInputResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct CreateUsersWithListInputBodyValidator<'a> { + #[validate] + body: &'a Vec, +} + +#[tracing::instrument(skip_all)] +fn create_users_with_list_input_validation( + body: Vec, +) -> std::result::Result<(Vec,), ValidationErrors> { + let b = CreateUsersWithListInputBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// CreateUsersWithListInput - POST /v2/user/createWithList +#[tracing::instrument(skip_all)] +async fn create_users_with_list_input( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || create_users_with_list_input_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .create_users_with_list_input(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + CreateUsersWithListInputResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn delete_user_validation( + path_params: models::DeleteUserPathParams, +) -> std::result::Result<(models::DeleteUserPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// DeleteUser - DELETE /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn delete_user( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || delete_user_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .delete_user(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DeleteUserResponse::InvalidUsernameSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + DeleteUserResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_user_by_name_validation( + path_params: models::GetUserByNamePathParams, +) -> std::result::Result<(models::GetUserByNamePathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} + +/// GetUserByName - GET /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn get_user_by_name( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_user_by_name_validation(path_params)) + .await + .unwrap(); + + let Ok((path_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_user_by_name(method, host, cookies, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetUserByNameResponse::SuccessfulOperation(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + GetUserByNameResponse::InvalidUsernameSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + GetUserByNameResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn login_user_validation( + query_params: models::LoginUserQueryParams, +) -> std::result::Result<(models::LoginUserQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} + +/// LoginUser - GET /v2/user/login +#[tracing::instrument(skip_all)] +async fn login_user( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || login_user_validation(query_params)) + .await + .unwrap(); + + let Ok((query_params,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .login_user(method, host, cookies, query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + LoginUserResponse::SuccessfulOperation { + body, + set_cookie, + x_rate_limit, + x_expires_after, + } => { + if let Some(set_cookie) = set_cookie { + let set_cookie = match header::IntoHeaderValue(set_cookie).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling set_cookie header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert(HeaderName::from_static("set-cookie"), set_cookie); + } + } + if let Some(x_rate_limit) = x_rate_limit { + let x_rate_limit = match header::IntoHeaderValue(x_rate_limit).try_into() { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling x_rate_limit header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("x-rate-limit"), x_rate_limit); + } + } + if let Some(x_expires_after) = x_expires_after { + let x_expires_after = match header::IntoHeaderValue(x_expires_after).try_into() + { + Ok(val) => val, + Err(e) => { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(format!("An internal server error occurred handling x_expires_after header - {}", e))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); + } + }; + + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers + .insert(HeaderName::from_static("x-expires-after"), x_expires_after); + } + } + + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/plain").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + LoginUserResponse::InvalidUsername => { + let mut response = response.status(400); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn logout_user_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// LogoutUser - GET /v2/user/logout +#[tracing::instrument(skip_all)] +async fn logout_user( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || logout_user_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().logout_user(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + LogoutUserResponse::SuccessfulOperation => { + let mut response = response.status(0); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct UpdateUserBodyValidator<'a> { + #[validate] + body: &'a models::User, +} + +#[tracing::instrument(skip_all)] +fn update_user_validation( + path_params: models::UpdateUserPathParams, + body: models::User, +) -> std::result::Result<(models::UpdateUserPathParams, models::User), ValidationErrors> { + path_params.validate()?; + let b = UpdateUserBodyValidator { body: &body }; + b.validate()?; + + Ok((path_params, body)) +} + +/// UpdateUser - PUT /v2/user/{username} +#[tracing::instrument(skip_all)] +async fn update_user( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || update_user_validation(path_params, body)) + .await + .unwrap(); + + let Ok((path_params, body)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .update_user(method, host, cookies, path_params, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + UpdateUserResponse::InvalidUserSupplied => { + let mut response = response.status(400); + response.body(Body::empty()) + } + UpdateUserResponse::UserNotFound => { + let mut response = response.status(404); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/petstore/src/types.rs b/samples/server/petstore/rust-axum/output/petstore/src/types.rs new file mode 100644 index 00000000000..eca60521b9a --- /dev/null +++ b/samples/server/petstore/rust-axum/output/petstore/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use petstore::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use petstore::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use petstore::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/.gitignore b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml b/samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml new file mode 100644 index 00000000000..fa0a6c1f3ed --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "ping-bearer-auth" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md b/samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md new file mode 100644 index 00000000000..46a6534028d --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md @@ -0,0 +1,91 @@ +# Rust API for ping-bearer-auth + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0 + + + + +This autogenerated project defines an API crate `ping-bearer-auth` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl ping-bearer-auth::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = ping-bearer-auth::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/header.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/lib.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/lib.rs new file mode 100644 index 00000000000..650c578724f --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/lib.rs @@ -0,0 +1,51 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "1.0"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum PingGetResponse { + /// OK + OK, +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// PingGet - GET /ping + async fn ping_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/models.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/models.rs new file mode 100644 index 00000000000..58fc026395c --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/models.rs @@ -0,0 +1,8 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/server/mod.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/server/mod.rs new file mode 100644 index 00000000000..b182b7d306d --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/server/mod.rs @@ -0,0 +1,80 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{Api, PingGetResponse}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/ping", get(ping_get::)) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn ping_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// PingGet - GET /ping +#[tracing::instrument(skip_all)] +async fn ping_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || ping_get_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().ping_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + PingGetResponse::OK => { + let mut response = response.status(201); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/types.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/types.rs new file mode 100644 index 00000000000..391471861d0 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use ping_bearer_auth::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/.gitignore b/samples/server/petstore/rust-axum/output/rust-axum-test/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/FILES new file mode 100644 index 00000000000..ea1c5b8c5be --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.gitignore +Cargo.toml +README.md +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/VERSION new file mode 100644 index 00000000000..fff4bdd7ab5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml b/samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml new file mode 100644 index 00000000000..347c1aad2d7 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "rust-server-test" +version = "2.3.4" +authors = ["OpenAPI Generator team and contributors"] +description = "This spec is for testing rust-server-specific things" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.21" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.16", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/README.md b/samples/server/petstore/rust-axum/output/rust-axum-test/README.md new file mode 100644 index 00000000000..b71f8cde216 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/README.md @@ -0,0 +1,91 @@ +# Rust API for rust-server-test + +This spec is for testing rust-server-specific things + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 2.3.4 + + + + +This autogenerated project defines an API crate `rust-server-test` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl rust-server-test::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = rust-server-test::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/header.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/header.rs @@ -0,0 +1,197 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!( + "Unable to parse {} as a string: {}", + stringify!($t), + e + )), + }, + Err(e) => Err(format!( + "Unable to parse header {:?} as a string - {}", + hdr_value, e + )), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect(), + )), + Err(e) => Err(format!( + "Unable to parse header: {:?} as a string - {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} into a header - {}", + hdr_value, e + )), + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert {:?} from a header {}", + hdr_value, e + )), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert: {:?} into a header: {}", + hdr_value, e + )), + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", hdr_value, e)), + }, + Err(e) => Err(format!( + "Unable to convert header {:?} to string {}", + hdr_value, e + )), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!( + "Unable to convert {:?} to a header: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/lib.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/lib.rs new file mode 100644 index 00000000000..9f3082c5b35 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/lib.rs @@ -0,0 +1,177 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_imports, + unused_extern_crates, + non_camel_case_types +)] +#![allow(unused_imports, unused_attributes)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use types::*; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "2.3.4"; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum AllOfGetResponse { + /// OK + OK(models::AllOfObject), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum DummyGetResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum DummyPutResponse { + /// Success + Success, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum FileResponseGetResponse { + /// Success + Success(ByteArray), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GetStructuredYamlResponse { + /// OK + OK(String), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum HtmlPostResponse { + /// Success + Success(String), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum PostYamlResponse { + /// OK + OK, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum RawJsonGetResponse { + /// Success + Success(crate::types::Object), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum SoloObjectPostResponse { + /// OK + OK, +} + +/// API +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Api { + /// AllOfGet - GET /allOf + async fn all_of_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// A dummy endpoint to make the spec valid.. + /// + /// DummyGet - GET /dummy + async fn dummy_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// DummyPut - PUT /dummy + async fn dummy_put( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::DummyPutRequest, + ) -> Result; + + /// Get a file. + /// + /// FileResponseGet - GET /file_response + async fn file_response_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// GetStructuredYaml - GET /get-structured-yaml + async fn get_structured_yaml( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Test HTML handling. + /// + /// HtmlPost - POST /html + async fn html_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: String, + ) -> Result; + + /// PostYaml - POST /post-yaml + async fn post_yaml( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: String, + ) -> Result; + + /// Get an arbitrary JSON blob.. + /// + /// RawJsonGet - GET /raw_json + async fn raw_json_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + ) -> Result; + + /// Send an arbitrary JSON blob. + /// + /// SoloObjectPost - POST /solo-object + async fn solo_object_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: crate::types::Object, + ) -> Result; +} + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/models.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/models.rs new file mode 100644 index 00000000000..edd388bdec1 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/models.rs @@ -0,0 +1,1071 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ANullableContainer { + #[serde(rename = "NullableThing")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_thing: Option>, + + #[serde(rename = "RequiredNullableThing")] + pub required_nullable_thing: Nullable, +} + +impl ANullableContainer { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_nullable_thing: Nullable) -> ANullableContainer { + ANullableContainer { + nullable_thing: None, + required_nullable_thing, + } + } +} + +/// Converts the ANullableContainer value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ANullableContainer { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.nullable_thing.as_ref().map(|nullable_thing| { + [ + "NullableThing".to_string(), + nullable_thing + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ] + .join(",") + }), + Some("RequiredNullableThing".to_string()), + Some( + self.required_nullable_thing + .as_ref() + .map_or("null".to_string(), |x| x.to_string()), + ), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ANullableContainer value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ANullableContainer { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub nullable_thing: Vec, + pub required_nullable_thing: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ANullableContainer".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "NullableThing" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in ANullableContainer".to_string()), + "RequiredNullableThing" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in ANullableContainer".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing ANullableContainer".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ANullableContainer { + nullable_thing: std::result::Result::Err( + "Nullable types not supported in ANullableContainer".to_string(), + )?, + required_nullable_thing: std::result::Result::Err( + "Nullable types not supported in ANullableContainer".to_string(), + )?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ANullableContainer - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ANullableContainer - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// An additionalPropertiesObject +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AdditionalPropertiesObject(std::collections::HashMap); + +impl validator::Validate for AdditionalPropertiesObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +impl std::convert::From> for AdditionalPropertiesObject { + fn from(x: std::collections::HashMap) -> Self { + AdditionalPropertiesObject(x) + } +} + +impl std::convert::From for std::collections::HashMap { + fn from(x: AdditionalPropertiesObject) -> Self { + x.0 + } +} + +impl std::ops::Deref for AdditionalPropertiesObject { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } +} + +impl std::ops::DerefMut for AdditionalPropertiesObject { + fn deref_mut(&mut self) -> &mut std::collections::HashMap { + &mut self.0 + } +} + +/// Converts the AdditionalPropertiesObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl ::std::string::ToString for AdditionalPropertiesObject { + fn to_string(&self) -> String { + // Skipping additionalProperties in query parameter serialization + "".to_string() + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AdditionalPropertiesObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl ::std::str::FromStr for AdditionalPropertiesObject { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Err( + "Parsing additionalProperties for AdditionalPropertiesObject is not supported", + ) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AllOfObject { + #[serde(rename = "sampleProperty")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sample_property: Option, + + #[serde(rename = "sampleBaseProperty")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sample_base_property: Option, +} + +impl AllOfObject { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AllOfObject { + AllOfObject { + sample_property: None, + sample_base_property: None, + } + } +} + +/// Converts the AllOfObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AllOfObject { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.sample_property.as_ref().map(|sample_property| { + ["sampleProperty".to_string(), sample_property.to_string()].join(",") + }), + self.sample_base_property + .as_ref() + .map(|sample_base_property| { + [ + "sampleBaseProperty".to_string(), + sample_base_property.to_string(), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AllOfObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AllOfObject { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub sample_property: Vec, + pub sample_base_property: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AllOfObject".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "sampleProperty" => intermediate_rep.sample_property.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "sampleBaseProperty" => intermediate_rep.sample_base_property.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AllOfObject".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AllOfObject { + sample_property: intermediate_rep.sample_property.into_iter().next(), + sample_base_property: intermediate_rep.sample_base_property.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AllOfObject - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AllOfObject - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct BaseAllOf { + #[serde(rename = "sampleBaseProperty")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sample_base_property: Option, +} + +impl BaseAllOf { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> BaseAllOf { + BaseAllOf { + sample_base_property: None, + } + } +} + +/// Converts the BaseAllOf value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for BaseAllOf { + fn to_string(&self) -> String { + let params: Vec> = + vec![self + .sample_base_property + .as_ref() + .map(|sample_base_property| { + [ + "sampleBaseProperty".to_string(), + sample_base_property.to_string(), + ] + .join(",") + })]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a BaseAllOf value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for BaseAllOf { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub sample_base_property: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing BaseAllOf".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "sampleBaseProperty" => intermediate_rep.sample_base_property.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing BaseAllOf".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(BaseAllOf { + sample_base_property: intermediate_rep.sample_base_property.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for BaseAllOf - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into BaseAllOf - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct DummyPutRequest { + #[serde(rename = "id")] + pub id: String, + + #[serde(rename = "password")] + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option, +} + +impl DummyPutRequest { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(id: String) -> DummyPutRequest { + DummyPutRequest { id, password: None } + } +} + +/// Converts the DummyPutRequest value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for DummyPutRequest { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("id".to_string()), + Some(self.id.to_string()), + self.password + .as_ref() + .map(|password| ["password".to_string(), password.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a DummyPutRequest value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for DummyPutRequest { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub password: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing DummyPutRequest".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "password" => intermediate_rep.password.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing DummyPutRequest".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(DummyPutRequest { + id: intermediate_rep + .id + .into_iter() + .next() + .ok_or_else(|| "id missing in DummyPutRequest".to_string())?, + password: intermediate_rep.password.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for DummyPutRequest - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into DummyPutRequest - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// structured response + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetYamlResponse { + /// Inner string + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +impl GetYamlResponse { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GetYamlResponse { + GetYamlResponse { value: None } + } +} + +/// Converts the GetYamlResponse value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GetYamlResponse { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .value + .as_ref() + .map(|value| ["value".to_string(), value.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GetYamlResponse value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GetYamlResponse { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub value: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GetYamlResponse".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "value" => intermediate_rep.value.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GetYamlResponse".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GetYamlResponse { + value: intermediate_rep.value.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GetYamlResponse - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GetYamlResponse - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +/// An object of objects + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectOfObjects { + #[serde(rename = "inner")] + #[serde(skip_serializing_if = "Option::is_none")] + pub inner: Option, +} + +impl ObjectOfObjects { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> ObjectOfObjects { + ObjectOfObjects { inner: None } + } +} + +/// Converts the ObjectOfObjects value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectOfObjects { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping inner in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectOfObjects value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectOfObjects { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inner: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectOfObjects".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "inner" => intermediate_rep.inner.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ObjectOfObjects".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectOfObjects { + inner: intermediate_rep.inner.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectOfObjects - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectOfObjects - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct ObjectOfObjectsInner { + #[serde(rename = "required_thing")] + pub required_thing: String, + + #[serde(rename = "optional_thing")] + #[serde(skip_serializing_if = "Option::is_none")] + pub optional_thing: Option, +} + +impl ObjectOfObjectsInner { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(required_thing: String) -> ObjectOfObjectsInner { + ObjectOfObjectsInner { + required_thing, + optional_thing: None, + } + } +} + +/// Converts the ObjectOfObjectsInner value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for ObjectOfObjectsInner { + fn to_string(&self) -> String { + let params: Vec> = vec![ + Some("required_thing".to_string()), + Some(self.required_thing.to_string()), + self.optional_thing.as_ref().map(|optional_thing| { + ["optional_thing".to_string(), optional_thing.to_string()].join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a ObjectOfObjectsInner value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for ObjectOfObjectsInner { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub required_thing: Vec, + pub optional_thing: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing ObjectOfObjectsInner".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "required_thing" => intermediate_rep.required_thing.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "optional_thing" => intermediate_rep.optional_thing.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing ObjectOfObjectsInner".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(ObjectOfObjectsInner { + required_thing: intermediate_rep + .required_thing + .into_iter() + .next() + .ok_or_else(|| "required_thing missing in ObjectOfObjectsInner".to_string())?, + optional_thing: intermediate_rep.optional_thing.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for ObjectOfObjectsInner - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into ObjectOfObjectsInner - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/server/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/server/mod.rs new file mode 100644 index 00000000000..8f8fb8024f0 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/server/mod.rs @@ -0,0 +1,653 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::models; + +use crate::{ + AllOfGetResponse, Api, DummyGetResponse, DummyPutResponse, FileResponseGetResponse, + GetStructuredYamlResponse, HtmlPostResponse, PostYamlResponse, RawJsonGetResponse, + SoloObjectPostResponse, +}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: Api + 'static, +{ + // build our application with a route + Router::new() + .route("/allOf", get(all_of_get::)) + .route("/dummy", get(dummy_get::).put(dummy_put::)) + .route("/file_response", get(file_response_get::)) + .route("/get-structured-yaml", get(get_structured_yaml::)) + .route("/html", post(html_post::)) + .route("/post-yaml", post(post_yaml::)) + .route("/raw_json", get(raw_json_get::)) + .route("/solo-object", post(solo_object_post::)) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn all_of_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// AllOfGet - GET /allOf +#[tracing::instrument(skip_all)] +async fn all_of_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || all_of_get_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().all_of_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + AllOfGetResponse::OK(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn dummy_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// DummyGet - GET /dummy +#[tracing::instrument(skip_all)] +async fn dummy_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || dummy_get_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().dummy_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DummyGetResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct DummyPutBodyValidator<'a> { + #[validate] + body: &'a models::DummyPutRequest, +} + +#[tracing::instrument(skip_all)] +fn dummy_put_validation( + body: models::DummyPutRequest, +) -> std::result::Result<(models::DummyPutRequest,), ValidationErrors> { + let b = DummyPutBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// DummyPut - PUT /dummy +#[tracing::instrument(skip_all)] +async fn dummy_put( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || dummy_put_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .dummy_put(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + DummyPutResponse::Success => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn file_response_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// FileResponseGet - GET /file_response +#[tracing::instrument(skip_all)] +async fn file_response_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || file_response_get_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .file_response_get(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + FileResponseGetResponse::Success(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_structured_yaml_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// GetStructuredYaml - GET /get-structured-yaml +#[tracing::instrument(skip_all)] +async fn get_structured_yaml( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_structured_yaml_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .get_structured_yaml(method, host, cookies) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + GetStructuredYamlResponse::OK(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/yaml").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct HtmlPostBodyValidator<'a> { + body: &'a String, +} + +#[tracing::instrument(skip_all)] +fn html_post_validation(body: String) -> std::result::Result<(String,), ValidationErrors> { + Ok((body,)) +} + +/// HtmlPost - POST /html +#[tracing::instrument(skip_all)] +async fn html_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: String, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || html_post_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .html_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + HtmlPostResponse::Success(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("text/html").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = body; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct PostYamlBodyValidator<'a> { + body: &'a String, +} + +#[tracing::instrument(skip_all)] +fn post_yaml_validation(body: String) -> std::result::Result<(String,), ValidationErrors> { + Ok((body,)) +} + +/// PostYaml - POST /post-yaml +#[tracing::instrument(skip_all)] +async fn post_yaml( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + body: String, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || post_yaml_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .post_yaml(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + PostYamlResponse::OK => { + let mut response = response.status(204); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn raw_json_get_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} + +/// RawJsonGet - GET /raw_json +#[tracing::instrument(skip_all)] +async fn raw_json_get( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || raw_json_get_validation()) + .await + .unwrap(); + + let Ok(()) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().raw_json_get(method, host, cookies).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + RawJsonGetResponse::Success(body) => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("*/*").map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })?, + ); + } + + let body_content = tokio::task::spawn_blocking(move || { + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) + }) + .await + .unwrap()?; + response.body(Body::from(body_content)) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct SoloObjectPostBodyValidator<'a> { + body: &'a crate::types::Object, +} + +#[tracing::instrument(skip_all)] +fn solo_object_post_validation( + body: crate::types::Object, +) -> std::result::Result<(crate::types::Object,), ValidationErrors> { + let b = SoloObjectPostBodyValidator { body: &body }; + b.validate()?; + + Ok((body,)) +} + +/// SoloObjectPost - POST /solo-object +#[tracing::instrument(skip_all)] +async fn solo_object_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: Api, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || solo_object_post_validation(body)) + .await + .unwrap(); + + let Ok((body,)) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl + .as_ref() + .solo_object_post(method, host, cookies, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + SoloObjectPostResponse::OK => { + let mut response = response.status(204); + response.body(Body::empty()) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/types.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/types.rs new file mode 100644 index 00000000000..cebc173ddf8 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use rust_server_test::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use rust_server_test::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use rust_server_test::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/samples/server/petstore/rust-axum/pom.xml b/samples/server/petstore/rust-axum/pom.xml new file mode 100644 index 00000000000..61e50dcc15a --- /dev/null +++ b/samples/server/petstore/rust-axum/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + org.openapitools + RustServerTests + pom + 1.0-SNAPSHOT + Rust Petstore Sample + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + build + integration-test + + exec + + + cargo + + build + --examples + + + + + test + integration-test + + exec + + + cargo + + test + + + + + clippy + integration-test + + exec + + + cargo + + clippy + + + + + + + +