diff --git a/bin/configs/manual/rust-axum-array-params.yaml b/bin/configs/manual/rust-axum-array-params.yaml new file mode 100644 index 00000000000..4a414e62734 --- /dev/null +++ b/bin/configs/manual/rust-axum-array-params.yaml @@ -0,0 +1,11 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/rust-axum-array-params-test +inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/rust-axum-array-params-test.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: rust-axum-array-params-test +globalProperties: + skipFormModel: false +enablePostProcessFile: true diff --git a/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache b/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache index ba37ecdeb0d..8c35b86bd6f 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/Cargo.mustache @@ -41,7 +41,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -54,8 +54,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache index f64ef16400d..c90c8e20276 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache @@ -207,7 +207,6 @@ use crate::{models, types::*}; #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct {{{operationIdCamelCase}}}QueryParams { {{#queryParams}} - {{#vendorExtensions}} {{#description}} /// {{{.}}} {{/description}} @@ -266,6 +265,16 @@ use crate::{models, types::*}; {{/maxItems}} )] {{/hasValidation}} + {{#isArray}} + {{#isNullable}} + // Nullable array query parameters are not fully supported. + {{/isNullable}} + {{^required}} + #[serde(default)] + {{/required}} + pub {{{paramName}}}: {{{dataType}}}, + {{/isArray}} + {{^isArray}} {{#required}} pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/required}} @@ -277,7 +286,7 @@ use crate::{models, types::*}; #[serde(skip_serializing_if="Option::is_none")] pub {{{paramName}}}: Option<{{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}>, {{/required}} - {{/vendorExtensions}} + {{/isArray}} {{/queryParams}} } 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 index 6ca3300e4f1..3289e2e0f1c 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-imports.mustache @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; 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 index 030434ea022..da77458efee 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-operation.mustache @@ -23,7 +23,7 @@ async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}, {{/pathParams.size}} {{#queryParams.size}} - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, {{/queryParams.size}} State(api_impl): State, {{#vendorExtensions}} @@ -334,7 +334,7 @@ where {{#allowBlockingResponseSerialize}} let body_content = {{/allowBlockingResponseSerialize}} - serde_urlencoded::to_string(body).map_err(|e| { + serde_html_form::to_string(body).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }){{^allowBlockingResponseSerialize}}).await.unwrap(){{/allowBlockingResponseSerialize}}?; diff --git a/modules/openapi-generator/src/test/resources/3_0/rust/rust-axum-array-params-test.yaml b/modules/openapi-generator/src/test/resources/3_0/rust/rust-axum-array-params-test.yaml new file mode 100644 index 00000000000..340362d074c --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/rust/rust-axum-array-params-test.yaml @@ -0,0 +1,24 @@ +openapi: 3.0.1 +info: + title: Test to check that array query params are rendered correctly. + version: 0.0.1 +paths: + /endpoint: + get: + description: Some endpoint. + parameters: + - name: numbers + in: query + description: Some numbers. + schema: + type: array + items: + type: number + - name: multiplier + in: query + description: Multipler for sum. + schema: + type: number + responses: + '200': + description: OK. diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/apikey-auths/.openapi-generator/VERSION index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/apikey-auths/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml b/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml index d99bd53b1fe..b297be95f69 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/README.md b/samples/server/petstore/rust-axum/output/apikey-auths/README.md index 9cf63a6d09d..a3ceb10fa4a 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/README.md +++ b/samples/server/petstore/rust-axum/output/apikey-auths/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/server/mod.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/server/mod.rs index e90e66b38ab..bb5c2f32d6a 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/src/server/mod.rs +++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/server/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; 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 index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/multipart-v3/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml index 9b19309ec21..0eff592c86e 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/README.md b/samples/server/petstore/rust-axum/output/multipart-v3/README.md index 0451497fd56..14d934bec80 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/README.md +++ b/samples/server/petstore/rust-axum/output/multipart-v3/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 69caf83d53c..fd6d66fb8fa 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; 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 index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/openapi-v3/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml index 5dd596ed3f6..f5733d3a5f0 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/README.md b/samples/server/petstore/rust-axum/output/openapi-v3/README.md index 813a1753c31..19081bbc45f 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-axum/output/openapi-v3/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 9ca228cebfd..a45adbcbb02 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs @@ -13,8 +13,8 @@ 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>, + #[serde(default)] + pub any_of: Vec, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] @@ -28,8 +28,8 @@ pub struct CallbackWithHeaderPostQueryParams { #[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>, + #[serde(default)] + pub list_of_strings: Vec, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] @@ -50,8 +50,8 @@ pub struct GetWithBooleanParameterQueryParams { #[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>, + #[serde(default)] + pub list_of_strings: Vec, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] @@ -80,8 +80,8 @@ pub struct ParamgetGetQueryParams { 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>, + #[serde(default)] + pub some_list: Vec, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] 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 index 41dda5958d9..7eacaecabd5 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; @@ -127,7 +127,7 @@ async fn any_of_get( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -253,7 +253,7 @@ async fn callback_with_header_post( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -314,7 +314,7 @@ async fn complex_query_param_get( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -505,7 +505,7 @@ async fn get_with_boolean_parameter( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -566,7 +566,7 @@ async fn json_complex_query_param_get( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -1247,7 +1247,7 @@ async fn paramget_get( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -1381,7 +1381,7 @@ async fn register_callback_post( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where 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 index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/ops-v3/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml b/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml index b4cb37fab58..13e249edd96 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/ops-v3/README.md b/samples/server/petstore/rust-axum/output/ops-v3/README.md index e22d2151321..5b11e0823e1 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/README.md +++ b/samples/server/petstore/rust-axum/output/ops-v3/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 857ac122719..809270d4ddb 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; 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 index de37f5c4cf5..96cfbb19ae2 100644 --- 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 @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT 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 index ea81193f6c5..8c6d24436b3 100644 --- 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 @@ -21,7 +21,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -34,8 +34,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", 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 index 32026c65f85..4920e04c815 100644 --- 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 @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 47e78e88d90..812eff3f5cb 100644 --- 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 @@ -34,8 +34,8 @@ 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>, + #[serde(default)] + pub enum_query_string_array: Vec, /// Query parameter enum test (string) /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "enum_query_string")] 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 index 67aea062535..405d2017a5d 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; @@ -804,7 +804,7 @@ async fn test_body_with_query_params( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, Json(body): Json, ) -> Result @@ -1074,7 +1074,7 @@ async fn test_enum_parameters( host: Host, cookies: CookieJar, headers: HeaderMap, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, Form(body): Form>, ) -> Result @@ -1605,7 +1605,7 @@ async fn find_pets_by_status( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -1686,7 +1686,7 @@ async fn find_pets_by_tags( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -2815,7 +2815,7 @@ async fn login_user( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where diff --git a/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/petstore/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/petstore/Cargo.toml b/samples/server/petstore/rust-axum/output/petstore/Cargo.toml index 3486b3a99ca..ddfb5dd9e07 100644 --- a/samples/server/petstore/rust-axum/output/petstore/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/petstore/Cargo.toml @@ -20,7 +20,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -33,8 +33,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/petstore/README.md b/samples/server/petstore/rust-axum/output/petstore/README.md index 9949db12c30..7f65e368f96 100644 --- a/samples/server/petstore/rust-axum/output/petstore/README.md +++ b/samples/server/petstore/rust-axum/output/petstore/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 95e7b4c2e91..5b2a6e03e45 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/server/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; @@ -269,7 +269,7 @@ async fn find_pets_by_status( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -350,7 +350,7 @@ async fn find_pets_by_tags( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where @@ -1551,7 +1551,7 @@ async fn login_user( method: Method, host: Host, cookies: CookieJar, - Query(query_params): Query, + QueryExtra(query_params): QueryExtra, State(api_impl): State, ) -> Result where 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 index de37f5c4cf5..96cfbb19ae2 100644 --- 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 @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT 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 index 6881b23a29d..cb11bcca61c 100644 --- a/samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", 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 index 580ec992064..c8b8e93b309 100644 --- a/samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index bad3941d438..1f3c2398441 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.gitignore b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-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-array-params-test/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator/FILES new file mode 100644 index 00000000000..285988aed79 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator/FILES @@ -0,0 +1,11 @@ +.gitignore +.openapi-generator-ignore +Cargo.toml +README.md +src/apis/default.rs +src/apis/mod.rs +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-array-params-test/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator/VERSION new file mode 100644 index 00000000000..96cfbb19ae2 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/Cargo.toml b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/Cargo.toml new file mode 100644 index 00000000000..b4f12bee0a9 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "rust-axum-array-params-test" +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.8", features = ["multipart"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } +base64 = "0.22" +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_html_form = "0.2" +serde_json = { version = "1", features = ["raw_value"] } +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.20", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/README.md b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/README.md new file mode 100644 index 00000000000..fcb53203d64 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/README.md @@ -0,0 +1,93 @@ +# Rust API for rust-axum-array-params-test + +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 +- Generator version: 7.13.0-SNAPSHOT + + + +This autogenerated project defines an API crate `rust-axum-array-params-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_axum_array_params_test::apis::default::Api for ServerImpl { + // API implementation goes here +} + +impl rust_axum_array_params_test::apis::ErrorHandler for ServerImpl {} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = rust_axum_array_params_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-array-params-test/src/apis/default.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/apis/default.rs new file mode 100644 index 00000000000..27663093e97 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/apis/default.rs @@ -0,0 +1,30 @@ +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Host}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use crate::{models, types::*}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum EndpointGetResponse { + /// OK. + Status200_OK, +} + +/// Default +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Default: super::ErrorHandler { + /// EndpointGet - GET /endpoint + async fn endpoint_get( + &self, + method: &Method, + host: &Host, + cookies: &CookieJar, + query_params: &models::EndpointGetQueryParams, + ) -> Result; +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/apis/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/apis/mod.rs new file mode 100644 index 00000000000..be654b5f14c --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/apis/mod.rs @@ -0,0 +1,21 @@ +pub mod default; + +// Error handler for unhandled errors. +#[async_trait::async_trait] +pub trait ErrorHandler { + #[allow(unused_variables)] + #[tracing::instrument(skip_all)] + async fn handle_error( + &self, + method: &::http::Method, + host: &axum_extra::extract::Host, + cookies: &axum_extra::extract::CookieJar, + error: E, + ) -> Result { + tracing::error!("Unhandled error: {:?}", error); + axum::response::Response::builder() + .status(http::StatusCode::INTERNAL_SERVER_ERROR) + .body(axum::body::Body::empty()) + .map_err(|_| http::StatusCode::INTERNAL_SERVER_ERROR) + } +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/header.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/header.rs new file mode 100644 index 00000000000..7c530892fbf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-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-array-params-test/src/lib.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/lib.rs new file mode 100644 index 00000000000..bfdd7c89923 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/lib.rs @@ -0,0 +1,28 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_extern_crates, + non_camel_case_types, + unused_imports, + unused_attributes +)] +#![allow( + clippy::derive_partial_eq_without_eq, + clippy::disallowed_names, + clippy::too_many_arguments +)] + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "0.0.1"; + +#[cfg(feature = "server")] +pub mod server; + +pub mod apis; +pub mod models; +pub mod types; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/models.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/models.rs new file mode 100644 index 00000000000..1901b7d8c75 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/models.rs @@ -0,0 +1,21 @@ +#![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 EndpointGetQueryParams { + /// Some numbers. + #[serde(rename = "numbers")] + #[serde(default)] + pub numbers: Vec, + /// Multipler for sum. + #[serde(rename = "multiplier")] + #[serde(skip_serializing_if = "Option::is_none")] + pub multiplier: Option, +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/server/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/server/mod.rs new file mode 100644 index 00000000000..770a62b1d45 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/server/mod.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; +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::{apis, models}; + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: apis::default::Default + Send + Sync + 'static, + E: std::fmt::Debug + Send + Sync + 'static, +{ + // build our application with a route + Router::new() + .route("/endpoint", get(endpoint_get::)) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn endpoint_get_validation( + query_params: models::EndpointGetQueryParams, +) -> std::result::Result<(models::EndpointGetQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} +/// EndpointGet - GET /endpoint +#[tracing::instrument(skip_all)] +async fn endpoint_get( + method: Method, + host: Host, + cookies: CookieJar, + QueryExtra(query_params): QueryExtra, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::default::Default + Send + Sync, + E: std::fmt::Debug + Send + Sync + 'static, +{ + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || endpoint_get_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() + .endpoint_get(&method, &host, &cookies, &query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::default::EndpointGetResponse::Status200_OK => { + let mut response = response.status(200); + response.body(Body::empty()) + } + }, + Err(why) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + return api_impl + .as_ref() + .handle_error(&method, &host, &cookies, why) + .await; + } + }; + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/types.rs b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/types.rs new file mode 100644 index 00000000000..1ff96e3b4f6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/rust-axum-array-params-test/src/types.rs @@ -0,0 +1,790 @@ +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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_test::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```should_panic + /// # use rust_axum_array_params_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_axum_array_params_test::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```should_panic + /// # use rust_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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_axum_array_params_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 Nullable<&T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use rust_axum_array_params_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_axum_array_params_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), + } + } +} + +impl validator::Validate for Nullable +where + T: validator::Validate, +{ + fn validate(&self) -> Result<(), validator::ValidationErrors> { + match self { + Self::Present(x) => x.validate(), + Self::Null => Ok(()), + } + } +} + +impl<'a, T> validator::ValidateArgs<'a> for Nullable +where + T: validator::ValidateArgs<'a>, +{ + type Args = T::Args; + fn validate_with_args(&self, args: Self::Args) -> Result<(), validator::ValidationErrors> { + match self { + Self::Present(x) => x.validate_with_args(args), + Self::Null => Ok(()), + } + } +} + +impl validator::ValidateEmail for Nullable +where + T: validator::ValidateEmail, +{ + fn as_email_string(&self) -> Option> { + match self { + Self::Present(x) => x.as_email_string(), + Self::Null => None, + } + } +} + +impl validator::ValidateUrl for Nullable +where + T: validator::ValidateUrl, +{ + fn as_url_string(&self) -> Option> { + match self { + Self::Present(x) => x.as_url_string(), + Self::Null => None, + } + } +} + +impl validator::ValidateContains for Nullable +where + T: validator::ValidateContains, +{ + fn validate_contains(&self, needle: &str) -> bool { + match self { + Self::Present(x) => x.validate_contains(needle), + Self::Null => true, + } + } +} + +impl validator::ValidateRequired for Nullable +where + T: validator::ValidateRequired, +{ + fn is_some(&self) -> bool { + self.is_present() + } +} + +impl validator::ValidateRegex for Nullable +where + T: validator::ValidateRegex, +{ + fn validate_regex(&self, regex: impl validator::AsRegex) -> bool { + match self { + Self::Present(x) => x.validate_regex(regex), + Self::Null => true, + } + } +} + +impl validator::ValidateRange for Nullable +where + T: validator::ValidateRange, +{ + fn greater_than(&self, max: I) -> Option { + use validator::ValidateRange; + match self { + Self::Present(x) => x.greater_than(max), + Self::Null => None, + } + } + fn less_than(&self, min: I) -> Option { + use validator::ValidateRange; + match self { + Self::Present(x) => x.less_than(min), + Self::Null => None, + } + } +} + +impl validator::ValidateLength for Nullable +where + T: validator::ValidateLength, + I: PartialEq + PartialOrd, +{ + fn length(&self) -> Option { + use validator::ValidateLength; + match self { + Self::Present(x) => x.length(), + Self::Null => None, + } + } +} + +impl From> for Option { + fn from(value: Nullable) -> Option { + match value { + Nullable::Present(x) => Some(x), + Nullable::Null => None, + } + } +} + +#[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-header-uuid/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/.openapi-generator/VERSION index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/Cargo.toml b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/Cargo.toml index dc3fc8fbb86..f5bb057bbf1 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/README.md b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/README.md index 880ec2323a1..8c6e2cc5bba 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/README.md +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/README.md @@ -12,7 +12,7 @@ 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.1.9 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/server/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/server/mod.rs index 23ca65d8f5f..7d9a061f8e6 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/server/mod.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/server/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/rust-axum-oneof/.openapi-generator/VERSION index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/Cargo.toml b/samples/server/petstore/rust-axum/output/rust-axum-oneof/Cargo.toml index fc3aaa306dd..3226402e595 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/README.md b/samples/server/petstore/rust-axum/output/rust-axum-oneof/README.md index 6c8dfa29b8f..7e37a4499da 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/README.md +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/server/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/server/mod.rs index 4f2a9e6f05b..de5ef9d4968 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/server/mod.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/server/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; 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 index de37f5c4cf5..96cfbb19ae2 100644 --- 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 @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT 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 index cb26009936a..aeb1b385d92 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", 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 index d6a8781dfe3..6a20fd0cc18 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-test/README.md +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT 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 index 5dbc1468a59..e2f452b1e94 100644 --- 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 @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error; diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/.openapi-generator/VERSION index de37f5c4cf5..96cfbb19ae2 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/.openapi-generator/VERSION +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0-SNAPSHOT +7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/Cargo.toml b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/Cargo.toml index 0d1de602033..8c50febe8cc 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/Cargo.toml @@ -19,7 +19,7 @@ conversion = [ [dependencies] async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } -axum-extra = { version = "0.10", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie", "query"] } base64 = "0.22" bytes = "1" chrono = { version = "0.4", features = ["serde"] } @@ -32,8 +32,8 @@ http = "1" lazy_static = "1" regex = "1" serde = { version = "1", features = ["derive"] } +serde_html_form = "0.2" serde_json = { version = "1", features = ["raw_value"] } -serde_urlencoded = "0.7" tokio = { version = "1", default-features = false, features = [ "signal", "rt-multi-thread", diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/README.md b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/README.md index 1699a54f8b4..07872ebc3c9 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/README.md +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/README.md @@ -12,7 +12,7 @@ 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 -- Generator version: 7.12.0-SNAPSHOT +- Generator version: 7.13.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/server/mod.rs b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/server/mod.rs index d3c7b6dd3ac..baf5fed751f 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/server/mod.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/server/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use axum::{body::Body, extract::*, response::Response, routing::*}; -use axum_extra::extract::{CookieJar, Host}; +use axum_extra::extract::{CookieJar, Host, Query as QueryExtra}; use bytes::Bytes; use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; use tracing::error;