From 777b7eeea064ab0ba22242ca2d47e93de0c2084c Mon Sep 17 00:00:00 2001 From: Linh Tran Tuan Date: Tue, 22 Jul 2025 18:43:46 +0900 Subject: [PATCH] [Rust-Axum] Basic Authorization - Basic Analytic - XSS Sanitize (#21578) * Update * Update * Update * Update * Update * Update --- .../rust-axum-apikey-authorization.yaml | 14 + .../languages/RustAxumServerCodegen.java | 54 +- .../main/resources/rust-axum/Cargo.mustache | 3 +- .../resources/rust-axum/apis-mod.mustache | 33 + .../main/resources/rust-axum/apis.mustache | 90 +- .../main/resources/rust-axum/header.mustache | 53 +- .../main/resources/rust-axum/models.mustache | 327 ++++++- .../resources/rust-axum/server-mod.mustache | 2 + .../rust-axum/server-operation.mustache | 106 ++- .../resources/rust-axum/server-route.mustache | 2 +- .../resources/rust-axum/server-utils.mustache | 8 + .../main/resources/rust-axum/types.mustache | 4 +- pom.xml | 17 +- .../output/apikey-authorization/.gitignore | 2 + .../.openapi-generator-ignore | 23 + .../.openapi-generator/FILES | 10 + .../.openapi-generator/VERSION | 1 + .../output/apikey-authorization/Cargo.toml | 47 + .../output/apikey-authorization/README.md | 93 ++ .../apikey-authorization/src/apis/mod.rs | 98 ++ .../apikey-authorization/src/apis/payments.rs | 128 +++ .../output/apikey-authorization/src/header.rs | 188 ++++ .../output/apikey-authorization/src/lib.rs | 28 + .../output/apikey-authorization/src/models.rs | 891 ++++++++++++++++++ .../apikey-authorization/src/server/mod.rs | 545 +++++++++++ .../output/apikey-authorization/src/types.rs | 790 ++++++++++++++++ .../apikey-auths/.openapi-generator/VERSION | 2 +- .../rust-axum/output/apikey-auths/Cargo.toml | 3 +- .../rust-axum/output/apikey-auths/README.md | 2 +- .../output/apikey-auths/src/apis/payments.rs | 3 + .../output/apikey-auths/src/header.rs | 35 +- .../output/apikey-auths/src/models.rs | 132 ++- .../output/apikey-auths/src/server/mod.rs | 26 +- .../output/apikey-auths/src/types.rs | 6 +- .../multipart-v3/.openapi-generator/VERSION | 2 +- .../rust-axum/output/multipart-v3/Cargo.toml | 3 +- .../rust-axum/output/multipart-v3/README.md | 2 +- .../output/multipart-v3/src/apis/default.rs | 3 + .../output/multipart-v3/src/header.rs | 35 +- .../output/multipart-v3/src/models.rs | 128 ++- .../output/multipart-v3/src/server/mod.rs | 11 +- .../output/multipart-v3/src/types.rs | 6 +- .../openapi-v3/.openapi-generator/VERSION | 2 +- .../rust-axum/output/openapi-v3/Cargo.toml | 3 +- .../rust-axum/output/openapi-v3/README.md | 2 +- .../output/openapi-v3/src/apis/default.rs | 48 + .../output/openapi-v3/src/apis/info_repo.rs | 1 + .../output/openapi-v3/src/apis/repo.rs | 1 + .../rust-axum/output/openapi-v3/src/header.rs | 35 +- .../rust-axum/output/openapi-v3/src/models.rs | 318 ++++--- .../output/openapi-v3/src/server/mod.rs | 134 ++- .../rust-axum/output/openapi-v3/src/types.rs | 6 +- .../output/ops-v3/.openapi-generator/VERSION | 2 +- .../rust-axum/output/ops-v3/Cargo.toml | 3 +- .../rust-axum/output/ops-v3/README.md | 2 +- .../output/ops-v3/src/apis/default.rs | 37 + .../rust-axum/output/ops-v3/src/header.rs | 35 +- .../rust-axum/output/ops-v3/src/models.rs | 54 ++ .../rust-axum/output/ops-v3/src/server/mod.rs | 11 +- .../rust-axum/output/ops-v3/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../Cargo.toml | 3 +- .../README.md | 2 +- .../src/apis/another_fake.rs | 1 + .../src/apis/fake.rs | 13 + .../src/apis/fake_classname_tags123.rs | 1 + .../src/apis/pet.rs | 8 + .../src/apis/store.rs | 4 + .../src/apis/user.rs | 8 + .../src/header.rs | 35 +- .../src/models.rs | 791 ++++++++-------- .../src/server/mod.rs | 38 +- .../src/types.rs | 6 +- .../petstore/.openapi-generator/VERSION | 2 +- .../rust-axum/output/petstore/Cargo.toml | 3 +- .../rust-axum/output/petstore/README.md | 2 +- .../rust-axum/output/petstore/src/apis/pet.rs | 8 + .../output/petstore/src/apis/store.rs | 4 + .../output/petstore/src/apis/user.rs | 8 + .../rust-axum/output/petstore/src/header.rs | 35 +- .../rust-axum/output/petstore/src/models.rs | 185 ++-- .../output/petstore/src/server/mod.rs | 59 +- .../rust-axum/output/petstore/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../output/ping-bearer-auth/Cargo.toml | 3 +- .../output/ping-bearer-auth/README.md | 2 +- .../ping-bearer-auth/src/apis/default.rs | 1 + .../output/ping-bearer-auth/src/header.rs | 35 +- .../output/ping-bearer-auth/src/models.rs | 54 ++ .../output/ping-bearer-auth/src/server/mod.rs | 16 +- .../output/ping-bearer-auth/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../rust-axum-array-params-test/Cargo.toml | 3 +- .../rust-axum-array-params-test/README.md | 2 +- .../src/apis/default.rs | 1 + .../rust-axum-array-params-test/src/header.rs | 35 +- .../rust-axum-array-params-test/src/models.rs | 54 ++ .../src/server/mod.rs | 11 +- .../rust-axum-array-params-test/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../output/rust-axum-header-uuid/Cargo.toml | 3 +- .../output/rust-axum-header-uuid/README.md | 2 +- .../rust-axum-header-uuid/src/apis/default.rs | 1 + .../rust-axum-header-uuid/src/header.rs | 43 +- .../rust-axum-header-uuid/src/models.rs | 54 ++ .../rust-axum-header-uuid/src/server/mod.rs | 13 +- .../output/rust-axum-header-uuid/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../output/rust-axum-oneof/Cargo.toml | 3 +- .../output/rust-axum-oneof/README.md | 2 +- .../rust-axum-oneof/src/apis/default.rs | 1 + .../output/rust-axum-oneof/src/header.rs | 35 +- .../output/rust-axum-oneof/src/models.rs | 140 ++- .../output/rust-axum-oneof/src/server/mod.rs | 11 +- .../output/rust-axum-oneof/src/types.rs | 6 +- .../rust-axum-test/.openapi-generator/VERSION | 2 +- .../output/rust-axum-test/Cargo.toml | 3 +- .../rust-axum/output/rust-axum-test/README.md | 2 +- .../output/rust-axum-test/src/apis/default.rs | 9 + .../output/rust-axum-test/src/header.rs | 35 +- .../output/rust-axum-test/src/models.rs | 181 ++-- .../output/rust-axum-test/src/server/mod.rs | 11 +- .../output/rust-axum-test/src/types.rs | 6 +- .../.openapi-generator/VERSION | 2 +- .../rust-axum-validation-test/Cargo.toml | 3 +- .../rust-axum-validation-test/README.md | 2 +- .../src/apis/default.rs | 1 + .../rust-axum-validation-test/src/header.rs | 35 +- .../rust-axum-validation-test/src/models.rs | 54 ++ .../src/server/mod.rs | 11 +- .../rust-axum-validation-test/src/types.rs | 6 +- 131 files changed, 5460 insertions(+), 1340 deletions(-) create mode 100644 bin/configs/manual/rust-axum-apikey-authorization.yaml create mode 100644 modules/openapi-generator/src/main/resources/rust-axum/server-utils.mustache create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/.gitignore create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator-ignore create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/FILES create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/VERSION create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/Cargo.toml create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/README.md create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/payments.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/header.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/lib.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/server/mod.rs create mode 100644 samples/server/petstore/rust-axum/output/apikey-authorization/src/types.rs diff --git a/bin/configs/manual/rust-axum-apikey-authorization.yaml b/bin/configs/manual/rust-axum-apikey-authorization.yaml new file mode 100644 index 00000000000..5f3b5e6000d --- /dev/null +++ b/bin/configs/manual/rust-axum-apikey-authorization.yaml @@ -0,0 +1,14 @@ +generatorName: rust-axum +outputDir: samples/server/petstore/rust-axum/output/apikey-authorization +inputSpec: modules/openapi-generator/src/test/resources/3_0/jetbrains/CheckoutBasicBearerCookieQueryHeaderBasicBearer.yaml +templateDir: modules/openapi-generator/src/main/resources/rust-axum +generateAliasAsModel: true +additionalProperties: + hideGenerationTimestamp: "true" + packageName: apikey-authorization + basicAuthorization: true + basicAnalytic: true + ownedRequest: true +globalProperties: + skipFormModel: false +enablePostProcessFile: true 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 index 725708a1a9a..69282a23e0c 100644 --- 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 @@ -61,6 +61,9 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege private Boolean allowBlockingValidator = false; private Boolean allowBlockingResponseSerialize = false; private String externCrateName; + private Boolean basicAuthorization = false; + private Boolean basicAnalytic = false; + private Boolean ownedRequest = false; // Types private static final String uuidType = "uuid::Uuid"; @@ -286,7 +289,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege LOGGER.info("Warning: Environment variable 'RUST_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI)."); } - if (!Boolean.TRUE.equals(ModelUtils.isGenerateAliasAsModel())) { + if (!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."); } @@ -316,6 +319,24 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege } else { additionalProperties.put("allowBlockingResponseSerialize", allowBlockingResponseSerialize); } + + if (additionalProperties.containsKey("basicAuthorization")) { + basicAuthorization = convertPropertyToBooleanAndWriteBack("basicAuthorization"); + } else { + additionalProperties.put("basicAuthorization", basicAuthorization); + } + + if (additionalProperties.containsKey("basicAnalytic")) { + basicAnalytic = convertPropertyToBooleanAndWriteBack("basicAnalytic"); + } else { + additionalProperties.put("basicAnalytic", basicAnalytic); + } + + if (additionalProperties.containsKey("ownedRequest")) { + ownedRequest = convertPropertyToBooleanAndWriteBack("ownedRequest"); + } else { + additionalProperties.put("ownedRequest", ownedRequest); + } } private void setPackageName(String packageName) { @@ -722,6 +743,21 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege operations.put("havingAuthMethod", true); operations.getOperation().forEach(op -> op.vendorExtensions.put("havingAuthMethod", true)); this.havingAuthMethods = true; + + if (basicAuthorization) { + operations.put("basicAuthorization", true); + operations.getOperation().forEach(op -> op.vendorExtensions.put("basicAuthorization", true)); + } + } + + if (basicAnalytic) { + operations.put("basicAnalytic", true); + operations.getOperation().forEach(op -> op.vendorExtensions.put("basicAnalytic", true)); + } + + if (ownedRequest) { + operations.put("ownedRequest", true); + operations.getOperation().forEach(op -> op.vendorExtensions.put("ownedRequest", true)); } return operationsMap; @@ -982,6 +1018,22 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege property.dataType = objectType; property.isNullable = false; } + + if (property.dataType.startsWith(vecType + "; + + pub mod convention { + pub const EVENT_SERVICE: &str = "_service_"; + pub const EVENT_ACTOR: &str = "_actor_"; + pub const EVENT_ACTION: &str = "_action_"; + pub const EVENT_RESOURCE_TYPE: &str = "_resource_type_"; + pub const EVENT_RESOURCE: &str = "_resource_"; + pub const EVENT_STATUS_CODE: &str = "_status_code_"; + pub const EVENT_LATENCY_SECS: &str = "_latency_secs_"; + pub const EVENT_TIMESTAMP: &str = "timestamp"; + } +} + +#[async_trait::async_trait] +pub trait EventDispatcher { + fn service_name(&self) -> String; + async fn dispatch(&self, event: event::Event) {} +} +{{/basicAnalytic}} + {{#authMethods}} {{#isApiKey}} {{#isKeyInCookie}} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/apis.mustache b/modules/openapi-generator/src/main/resources/rust-axum/apis.mustache index 1221f4c2a7d..2a6f11f009d 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/apis.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/apis.mustache @@ -15,6 +15,75 @@ use crate::{models, types::*}; {{/operations}} {{#operations}} + +{{#basicAuthorization}} +/// {{classnamePascalCase}} APIs - Authorization. +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait {{classnamePascalCase}}Authorization { + type Claims; + +{{#operation}} + {{#vendorExtensions}} + {{#x-has-auth-methods}} + {{#basicAuthorization}} + {{#vendorExtensions}} + /// Authorization{{#summary}} - {{{.}}}{{/summary}}. + /// {{{operationId}}} - {{{httpMethod}}} {{{basePathWithoutHost}}}{{{path}}} + async fn {{{x-operation-id}}}_authorize( + &self, + method: &Method, + host: &Host, + cookies: &CookieJar, + claims: &Self::Claims, + {{#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 { + Ok(super::Authorization::Authorized) + } + {{/vendorExtensions}} + {{/basicAuthorization}} + {{/x-has-auth-methods}} + {{/vendorExtensions}} + {{^-last}} + + {{/-last}} +{{/operation}} +} +{{/basicAuthorization}} + /// {{classnamePascalCase}} #[async_trait] #[allow(clippy::ptr_arg)] @@ -32,36 +101,37 @@ pub trait {{classnamePascalCase}}{{/required}}, + body: {{^ownedRequest}}&{{/ownedRequest}}{{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, {{/x-consumes-plain-text}} {{#x-consumes-plain-text}} {{#isString}} - body: &String, + body: {{^ownedRequest}}&{{/ownedRequest}}String, {{/isString}} {{^isString}} - body: &Bytes, + body: {{^ownedRequest}}&{{/ownedRequest}}Bytes, {{/isString}} {{/x-consumes-plain-text}} {{/vendorExtensions}} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/header.mustache b/modules/openapi-generator/src/main/resources/rust-axum/header.mustache index 36bfcf45f7b..761a2dc0ec2 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/header.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/header.mustache @@ -30,11 +30,10 @@ macro_rules! ihv_generate { 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!(r#"Unable to parse {} as a string: {e}"#, + stringify!($t))), }, - Err(e) => Err(format!("Unable to parse header {:?} as a string - {}", - hdr_value, e)), + Err(e) => Err(format!(r#"Unable to parse header {hdr_value:?} as a string - {e}"#)), } } } @@ -75,8 +74,7 @@ impl TryFrom for IntoHeaderValue> { y => Some(y.to_string()), }) .collect())), - Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}", - hdr_value, e)), + Err(e) => Err(format!(r#"Unable to parse header: {hdr_value:?} as a string - {e}"#)), } } } @@ -87,8 +85,7 @@ impl TryFrom>> for HeaderValue { 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)) + Err(e) => Err(format!(r#"Unable to convert {hdr_value:?} into a header - {e}"#)) } } } @@ -101,10 +98,9 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } - } + } } impl TryFrom> for HeaderValue { @@ -113,8 +109,7 @@ impl TryFrom> for HeaderValue { 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)) + Err(e) => Err(format!(r#"Unable to convert {hdr_value:?} from a header {e}"#)) } } } @@ -128,11 +123,9 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, - Err(e) => Err(format!("Unable to convert {:?} from a header {}", - hdr_value, e)), + Err(e) => Err(format!(r#"Unable to convert {hdr_value:?} from a header {e}"#)), } } } @@ -143,9 +136,8 @@ impl TryFrom> for HeaderValue { 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)) - } + Err(e) => Err(format!(r#"Unable to convert: {hdr_value:?} into a header: {e}"#)) + } } } @@ -158,11 +150,9 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, - Err(e) => Err(format!("Unable to convert header {:?} to string {}", - hdr_value, e)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to string {e}"#)), } } } @@ -173,8 +163,7 @@ impl TryFrom>> for HeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert {hdr_value:?} to a header: {e}"#)), } } } @@ -189,12 +178,9 @@ impl TryFrom for IntoHeaderValue { match hdr_value.to_str() { Ok(hdr_value) => match uuid::Uuid::from_str(hdr_value) { Ok(uuid) => Ok(IntoHeaderValue(uuid)), - Err(e) => Err(format!("Unable to parse: {} as uuid - {}", hdr_value, e)), + Err(e) => Err(format!(r#"Unable to parse: {hdr_value} as uuid - {e}"#)), }, - Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e - )), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to string {e}"#)), } } } @@ -205,10 +191,7 @@ impl TryFrom> for HeaderValue { fn try_from(hdr_value: IntoHeaderValue) -> Result { match HeaderValue::from_bytes(hdr_value.0.as_bytes()) { Ok(hdr_value) => Ok(hdr_value), - Err(e) => Err(format!( - "Unable to convert {:?} to a header: {}", - hdr_value, e - )), + Err(e) => Err(format!(r#"Unable to convert {hdr_value:?} to a header: {e}"#)), } } } 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 c90c8e20276..681d374d85a 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache @@ -7,12 +7,64 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map(v: &std::collections::HashMap) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + {{! 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)] @@ -68,10 +120,54 @@ use crate::{models, types::*}; length(min = {{minItems}}), {{/minItems}} {{/maxItems}} - )] + {{#vendorExtensions.is-nested}} + nested, + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + custom(function = "check_xss_string")), + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + nested, + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + custom(function = "check_xss_vec_string"), + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + custom(function = "check_xss_map_nested"), + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + custom(function = "check_xss_map"), + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + custom(function = "check_xss_map_string"), + {{/vendorExtensions.is-map-string}} + )] + {{/hasValidation}} + {{^hasValidation}} + {{#vendorExtensions.is-nested}} + #[validate(nested)] + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + #[validate(custom(function = "check_xss_string"))] + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + #[validate(nested) + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + #[validate(custom(function = "check_xss_vec_string"))] + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + #[validate(custom(function = "check_xss_map_nested"))] + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + #[validate(custom(function = "check_xss_map"))] + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + #[validate(custom(function = "check_xss_map_string"))] + {{/vendorExtensions.is-map-string}} {{/hasValidation}} pub {{{paramName}}}: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}}, - {{/headerParams}} + {{/headerParams}} } {{#headerParams}} @@ -97,15 +193,15 @@ use crate::{models, types::*}; {{/isByteArray}} {{/pattern}} {{/hasValidation}} - {{/headerParams}} - + {{/headerParams}} + {{/headerParams.size}} {{#pathParams.size}} #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] - #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct {{{operationIdCamelCase}}}PathParams { {{#pathParams}} - {{#description}} + {{#description}} /// {{{.}}} {{/description}} {{#isEnum}} @@ -160,7 +256,55 @@ use crate::{models, types::*}; length(min = {{minItems}}), {{/minItems}} {{/maxItems}} - )] + {{^isNullable}} + {{#vendorExtensions.is-nested}} + nested, + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + custom(function = "check_xss_string"), + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + nested, + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + custom(function = "check_xss_vec_string"), + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + custom(function = "check_xss_map_nested"), + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + custom(function = "check_xss_map"), + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + custom(function = "check_xss_map_string"), + {{/vendorExtensions.is-map-string}} + {{/isNullable}} + )] + {{/hasValidation}} + {{^hasValidation}} + {{^isNullable}} + {{#vendorExtensions.is-nested}} + #[validate(nested)] + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + #[validate(custom(function = "check_xss_string"))] + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + #[validate(nested)] + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + #[validate(custom(function = "check_xss_vec_string"))] + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + #[validate(custom(function = "check_xss_map_nested"))] + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + #[validate(custom(function = "check_xss_map"))] + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + #[validate(custom(function = "check_xss_map_string"))] + {{/vendorExtensions.is-map-string}} + {{/isNullable}} {{/hasValidation}} {{#required}} pub {{{paramName}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, @@ -204,10 +348,10 @@ use crate::{models, types::*}; {{/pathParams.size}} {{#queryParams.size}} #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] - #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct {{{operationIdCamelCase}}}QueryParams { {{#queryParams}} - {{#description}} + {{#description}} /// {{{.}}} {{/description}} {{#isEnum}} @@ -263,29 +407,77 @@ use crate::{models, types::*}; length(min = {{minItems}}), {{/minItems}} {{/maxItems}} - )] + {{^isNullable}} + {{#vendorExtensions.is-nested}} + nested, + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + custom(function = "check_xss_string"), + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + nested, + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + custom(function = "check_xss_vec_string"), + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + custom(function = "check_xss_map_nested"), + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + custom(function = "check_xss_map"), + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + custom(function = "check_xss_map_string"), + {{/vendorExtensions.is-map-string}} + {{/isNullable}} + )] + {{/hasValidation}} + {{^hasValidation}} + {{^isNullable}} + {{#vendorExtensions.is-nested}} + #[validate(nested)] + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + #[validate(custom(function = "check_xss_string"))] + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + #[validate(nested)] + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + #[validate(custom(function = "check_xss_vec_string"))] + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + #[validate(custom(function = "check_xss_map_nested"))] + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + #[validate(custom(function = "check_xss_map"))] + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + #[validate(custom(function = "check_xss_map_string"))] + {{/vendorExtensions.is-map-string}} + {{/isNullable}} {{/hasValidation}} {{#isArray}} - {{#isNullable}} - // Nullable array query parameters are not fully supported. - {{/isNullable}} - {{^required}} - #[serde(default)] - {{/required}} - pub {{{paramName}}}: {{{dataType}}}, - {{/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}} - {{^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}} + {{#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}} {{/isArray}} {{/queryParams}} } @@ -313,7 +505,7 @@ use crate::{models, types::*}; {{/isByteArray}} {{/pattern}} {{/hasValidation}} - {{/queryParams}} + {{/queryParams}} {{/queryParams.size}} {{/vendorExtensions}} {{/operation}} @@ -345,6 +537,13 @@ pub enum {{{classname}}} { {{/allowableValues}} } +impl validator::Validate for {{{classname}}} +{ + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + impl std::fmt::Display for {{{classname}}} { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { @@ -367,7 +566,7 @@ impl std::str::FromStr for {{{classname}}} { {{{value}}} => std::result::Result::Ok({{{classname}}}::{{{name}}}), {{/enumVars}} {{/allowableValues}} - _ => std::result::Result::Err(format!("Value not valid: {}", s)), + _ => std::result::Result::Err(format!(r#"Value not valid: {s}"#)), } } } @@ -665,7 +864,7 @@ impl std::str::FromStr for {{{classname}}} { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct {{{classname}}} { {{#vars}} -{{#description}} +{{#description}} /// {{{.}}} {{/description}} {{#isEnum}} @@ -725,7 +924,55 @@ pub struct {{{classname}}} { length(min = {{minItems}}), {{/minItems}} {{/maxItems}} - )] + {{^isNullable}} + {{#vendorExtensions.is-nested}} + nested, + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + custom(function = "check_xss_string"), + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + nested, + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + custom(function = "check_xss_vec_string"), + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + custom(function = "check_xss_map_nested"), + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + custom(function = "check_xss_map"), + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + custom(function = "check_xss_map_string"), + {{/vendorExtensions.is-map-string}} + {{/isNullable}} + )] +{{/hasValidation}} +{{^hasValidation}} + {{^isNullable}} + {{#vendorExtensions.is-nested}} + #[validate(nested)] + {{/vendorExtensions.is-nested}} + {{#vendorExtensions.is-string}} + #[validate(custom(function = "check_xss_string"))] + {{/vendorExtensions.is-string}} + {{#vendorExtensions.is-vec-nested}} + #[validate(nested)] + {{/vendorExtensions.is-vec-nested}} + {{#vendorExtensions.is-vec-string}} + #[validate(custom(function = "check_xss_vec_string"))] + {{/vendorExtensions.is-vec-string}} + {{#vendorExtensions.is-map-nested}} + #[validate(custom(function = "check_xss_map_nested"))] + {{/vendorExtensions.is-map-nested}} + {{#vendorExtensions.is-map}} + #[validate(custom(function = "check_xss_map"))] + {{/vendorExtensions.is-map}} + {{#vendorExtensions.is-map-string}} + #[validate(custom(function = "check_xss_map_string"))] + {{/vendorExtensions.is-map-string}} + {{/isNullable}} {{/hasValidation}} {{#required}} pub {{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, @@ -958,9 +1205,7 @@ impl std::convert::TryFrom> for HeaderV 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)) + std::result::Result::Err(e) => std::result::Result::Err(format!(r#"Invalid header value for {{classname}} - value: {hdr_value} is invalid {e}"#)) } } } @@ -974,14 +1219,10 @@ impl std::convert::TryFrom for header::IntoHeaderValue<{{{classname 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(err) => std::result::Result::Err(format!(r#"Unable to convert header value '{value}' into {{classname}} - {err}"#)) } }, - std::result::Result::Err(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(e) => std::result::Result::Err(format!(r#"Unable to convert header: {hdr_value:?} to string: {e}"#)) } } } 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 index c609c46b7db..5f03efa85c2 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-mod.mustache @@ -16,3 +16,5 @@ {{/operations}} {{/apis}} {{/apiInfo}} + +{{>server-utils}} \ No newline at end of file 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 c383f007a62..610786a37b0 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 @@ -59,9 +59,11 @@ async fn {{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}} Result where I: AsRef + Send + Sync, - A: apis::{{classFilename}}::{{classnamePascalCase}}{{#vendorExtensions}}{{#x-has-cookie-auth-methods}}+ apis::CookieAuthentication{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}+ apis::ApiKeyAuthHeader{{/x-has-header-auth-methods}}{{#x-has-basic-auth-methods}}+ apis::ApiAuthBasic{{/x-has-basic-auth-methods}}{{/vendorExtensions}} + Send + Sync, + A: {{#basicAnalytic}}apis::EventDispatcher + {{/basicAnalytic}}apis::{{classFilename}}::{{classnamePascalCase}}{{#havingAuthMethod}}{{#basicAuthorization}} + apis::{{classFilename}}::{{classnamePascalCase}}Authorization{{/basicAuthorization}}{{/havingAuthMethod}}{{#vendorExtensions}}{{#x-has-cookie-auth-methods}}+ apis::CookieAuthentication{{/x-has-cookie-auth-methods}}{{#x-has-header-auth-methods}}+ apis::ApiKeyAuthHeader{{/x-has-header-auth-methods}}{{#x-has-basic-auth-methods}}+ apis::ApiAuthBasic{{/x-has-basic-auth-methods}}{{/vendorExtensions}} + Send + Sync, E: std::fmt::Debug + Send + Sync + 'static, { +{{#basicAnalytic}}let start_at = chrono::Utc::now();{{/basicAnalytic}} + {{#vendorExtensions}} {{#x-has-auth-methods}} // Authentication @@ -88,10 +90,7 @@ where {{/x-has-basic-auth-methods}} ; let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; {{/x-has-auth-methods}} {{/vendorExtensions}} @@ -115,7 +114,7 @@ where 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 }); + .body(Body::from(format!("Invalid header {{{baseName}}} - {err}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); }, }, @@ -197,30 +196,81 @@ where }; {{/disableValidator}} - let result = api_impl.as_ref().{{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}( - &method, - &host, - &cookies, +{{#vendorExtensions}} +{{#x-has-auth-methods}} + {{#basicAuthorization}} + {{#vendorExtensions}} + // Authorization + let authorization = api_impl.as_ref().{{{x-operation-id}}}_authorize( + &method, + &host, + &cookies, + &claims, + {{#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; + match authorization { + Ok(authorization) => match authorization { + apis::Authorization::Authorized => {}, + apis::Authorization::Forbidden => { return response_with_status_code_only(StatusCode::FORBIDDEN); }, + }, + Err(_) => { return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); } + } + {{/vendorExtensions}} + {{/basicAuthorization}} +{{/x-has-auth-methods}} +{{/vendorExtensions}} + +{{#basicAnalytic}}let mut event = apis::event::Event::default();{{/basicAnalytic}} +let result = api_impl.as_ref().{{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}( + {{#basicAnalytic}}&mut event,{{/basicAnalytic}} + {{^ownedRequest}}&{{/ownedRequest}}method, + {{^ownedRequest}}&{{/ownedRequest}}host, + {{^ownedRequest}}&{{/ownedRequest}}cookies, {{#vendorExtensions}} {{#x-has-auth-methods}} - &claims, + {{^ownedRequest}}&{{/ownedRequest}}claims, {{/x-has-auth-methods}} {{/vendorExtensions}} {{#headerParams.size}} - &header_params, + {{^ownedRequest}}&{{/ownedRequest}}header_params, {{/headerParams.size}} {{#pathParams.size}} - &path_params, + {{^ownedRequest}}&{{/ownedRequest}}path_params, {{/pathParams.size}} {{#queryParams.size}} - &query_params, + {{^ownedRequest}}&{{/ownedRequest}}query_params, {{/queryParams.size}} {{#vendorExtensions}} {{^x-consumes-multipart-related}} {{^x-consumes-multipart}} {{#bodyParams}} {{#-first}} - &body, + {{^ownedRequest}}&{{/ownedRequest}}body, {{/-first}} {{/bodyParams}} {{/x-consumes-multipart}} @@ -276,7 +326,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling {{name}} header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -354,12 +404,28 @@ where }, {{/responses}} }, - 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; + Err({{#ownedRequest}}_{{/ownedRequest}}why) => { + {{^ownedRequest}} + // 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; + {{/ownedRequest}} + {{#ownedRequest}} + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + {{/ownedRequest}} }, }; + {{#basicAnalytic}} + if let Ok(resp) = resp.as_ref() && !event.is_empty() { + event.insert(apis::event::convention::EVENT_TIMESTAMP.to_string(), format!("{start_at:?}")); + event.insert(apis::event::convention::EVENT_SERVICE.to_string(), api_impl.as_ref().service_name()); + event.insert(apis::event::convention::EVENT_STATUS_CODE.to_string(), resp.status().as_u16().to_string()); + event.insert(apis::event::convention::EVENT_ACTION.to_string(), "{{#vendorExtensions}}{{{x-operation-id}}}{{/vendorExtensions}}".to_string()); + event.insert(apis::event::convention::EVENT_LATENCY_SECS.to_string(), format!("{:.6}", chrono::Utc::now().signed_duration_since(start_at).as_seconds_f64())); + api_impl.as_ref().dispatch(event).await; + } + {{/basicAnalytic}} + 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 index 333280de3f2..02167671476 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-route.mustache @@ -2,7 +2,7 @@ pub fn new(api_impl: I) -> Router where I: AsRef + Clone + Send + Sync + 'static, - A: {{#apiInfo}}{{#apis}}{{#operations}}apis::{{classFilename}}::{{classnamePascalCase}} + {{/operations}}{{/apis}}{{/apiInfo}}{{#authMethods}}{{#isApiKey}}{{#isKeyInCookie}}apis::CookieAuthentication + {{/isKeyInCookie}}{{#isKeyInHeader}}apis::ApiKeyAuthHeader + {{/isKeyInHeader}}{{/isApiKey}}{{#isBasic}}apis::ApiAuthBasic + {{/isBasic}}{{/authMethods}}Send + Sync + 'static, + A: {{#basicAnalytic}}apis::EventDispatcher + {{/basicAnalytic}}{{#apiInfo}}{{#apis}}{{#operations}}apis::{{classFilename}}::{{classnamePascalCase}}{{#havingAuthMethod}}{{#basicAuthorization}} + apis::{{classFilename}}::{{classnamePascalCase}}Authorization{{/basicAuthorization}}{{/havingAuthMethod}} + {{/operations}}{{/apis}}{{/apiInfo}}{{#authMethods}}{{#isApiKey}}{{#isKeyInCookie}}apis::CookieAuthentication + {{/isKeyInCookie}}{{#isKeyInHeader}}apis::ApiKeyAuthHeader + {{/isKeyInHeader}}{{/isApiKey}}{{#isBasic}}apis::ApiAuthBasic + {{/isBasic}}{{/authMethods}}Send + Sync + 'static, E: std::fmt::Debug + Send + Sync + 'static, {{#havingAuthMethods}}C: Send + Sync + 'static,{{/havingAuthMethods}} { diff --git a/modules/openapi-generator/src/main/resources/rust-axum/server-utils.mustache b/modules/openapi-generator/src/main/resources/rust-axum/server-utils.mustache new file mode 100644 index 00000000000..6a62707a53c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/rust-axum/server-utils.mustache @@ -0,0 +1,8 @@ +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/modules/openapi-generator/src/main/resources/rust-axum/types.mustache b/modules/openapi-generator/src/main/resources/rust-axum/types.mustache index fca7135cf44..024c96cc115 100644 --- a/modules/openapi-generator/src/main/resources/rust-axum/types.mustache +++ b/modules/openapi-generator/src/main/resources/rust-axum/types.mustache @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, diff --git a/pom.xml b/pom.xml index 5fc6707b4c2..bc514c7ca2a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,12 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> org.sonatype.oss oss-parent 5 - + 4.0.0 @@ -180,8 +180,10 @@ @{argLine} -XX:+StartAttachListener 2 - 1000 - 5000 + 1000 + + 5000 + @@ -509,7 +511,8 @@ min - ${project.basedir}${file.separator}spotbugs-exclude.xml + ${project.basedir}${file.separator}spotbugs-exclude.xml + @@ -1232,7 +1235,7 @@ 5.10.2 1.6.21 1.6.21 - 1.18.30 + 1.18.38 3.8.1 3.14.0 3.4.2 diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/.gitignore b/samples/server/petstore/rust-axum/output/apikey-authorization/.gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator-ignore b/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/.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/apikey-authorization/.openapi-generator/FILES b/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/FILES new file mode 100644 index 00000000000..282f205d116 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/FILES @@ -0,0 +1,10 @@ +.gitignore +Cargo.toml +README.md +src/apis/mod.rs +src/apis/payments.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/apikey-authorization/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/VERSION new file mode 100644 index 00000000000..fc74d6ceba8 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/Cargo.toml b/samples/server/petstore/rust-axum/output/apikey-authorization/Cargo.toml new file mode 100644 index 00000000000..9a9400315c2 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "apikey-authorization" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "Checkout Basic" +edition = "2024" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +ammonia = "4" +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/apikey-authorization/README.md b/samples/server/petstore/rust-axum/output/apikey-authorization/README.md new file mode 100644 index 00000000000..7891b626baf --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/README.md @@ -0,0 +1,93 @@ +# Rust API for apikey-authorization + +Checkout Basic + +## 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 +- Generator version: 7.15.0-SNAPSHOT + + + +This autogenerated project defines an API crate `apikey-authorization` 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 apikey_authorization::apis::default::Api for ServerImpl { + // API implementation goes here +} + +impl apikey_authorization::apis::ErrorHandler for ServerImpl {} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = apikey_authorization::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/apikey-authorization/src/apis/mod.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/mod.rs new file mode 100644 index 00000000000..c7a7083c9d7 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/mod.rs @@ -0,0 +1,98 @@ +pub mod payments; + +#[allow(dead_code)] +#[derive(Debug, Eq, PartialEq)] +pub enum Authorization { + Authorized, + Forbidden, +} + +pub mod event { + /// Anything to be recorded. + pub type Event = std::collections::HashMap; + + pub mod convention { + pub const EVENT_SERVICE: &str = "_service_"; + pub const EVENT_ACTOR: &str = "_actor_"; + pub const EVENT_ACTION: &str = "_action_"; + pub const EVENT_RESOURCE_TYPE: &str = "_resource_type_"; + pub const EVENT_RESOURCE: &str = "_resource_"; + pub const EVENT_STATUS_CODE: &str = "_status_code_"; + pub const EVENT_LATENCY_SECS: &str = "_latency_secs_"; + pub const EVENT_TIMESTAMP: &str = "timestamp"; + } +} + +#[async_trait::async_trait] +pub trait EventDispatcher { + fn service_name(&self) -> String; + async fn dispatch(&self, event: event::Event) {} +} + +/// API Key Authentication - Header. +#[async_trait::async_trait] +pub trait ApiKeyAuthHeader { + type Claims; + + /// Extracting Claims from Header. Return None if the Claims are invalid. + async fn extract_claims_from_header( + &self, + headers: &axum::http::header::HeaderMap, + key: &str, + ) -> Option; +} + +/// Cookie Authentication. +#[async_trait::async_trait] +pub trait CookieAuthentication { + type Claims; + + /// Extracting Claims from Cookie. Return None if the Claims are invalid. + async fn extract_claims_from_cookie( + &self, + cookies: &axum_extra::extract::CookieJar, + key: &str, + ) -> Option; +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum BasicAuthKind { + Basic, + Bearer, +} + +/// API Key Authentication - Authentication Header. +/// For `Basic token` and `Bearer token` +#[async_trait::async_trait] +pub trait ApiAuthBasic { + type Claims; + + /// Extracting Claims from Header. Return None if the Claims are invalid. + async fn extract_claims_from_auth_header( + &self, + kind: BasicAuthKind, + headers: &axum::http::header::HeaderMap, + key: &str, + ) -> Option; +} + +// 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/apikey-authorization/src/apis/payments.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/payments.rs new file mode 100644 index 00000000000..32766efa6d1 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/apis/payments.rs @@ -0,0 +1,128 @@ +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 GetPaymentMethodByIdResponse { + /// OK - the request has succeeded. + Status200_OK(models::PaymentMethod), + /// Unprocessable Entity - a request validation error. + Status422_UnprocessableEntity(models::CheckoutError), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GetPaymentMethodsResponse { + /// OK - the request has succeeded. + Status200_OK(Vec), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum PostMakePaymentResponse { + /// OK - the request has succeeded. + Status200_OK(models::PaymentResult), + /// Unprocessable Entity - a request validation error. + Status422_UnprocessableEntity(models::CheckoutError), +} + +/// Payments APIs - Authorization. +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait PaymentsAuthorization { + type Claims; + + /// Authorization - Get payment method by id. + /// GetPaymentMethodById - GET /v71/paymentMethods/{id} + async fn get_payment_method_by_id_authorize( + &self, + method: &Method, + host: &Host, + cookies: &CookieJar, + claims: &Self::Claims, + path_params: &models::GetPaymentMethodByIdPathParams, + ) -> Result { + Ok(super::Authorization::Authorized) + } + + /// Authorization - Get payment methods. + /// GetPaymentMethods - GET /v71/paymentMethods + async fn get_payment_methods_authorize( + &self, + method: &Method, + host: &Host, + cookies: &CookieJar, + claims: &Self::Claims, + ) -> Result { + Ok(super::Authorization::Authorized) + } + + /// Authorization - Make a payment. + /// PostMakePayment - POST /v71/payments + async fn post_make_payment_authorize( + &self, + method: &Method, + host: &Host, + cookies: &CookieJar, + claims: &Self::Claims, + body: &Option, + ) -> Result { + Ok(super::Authorization::Authorized) + } +} + +/// Payments +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Payments: + super::ErrorHandler +{ + type Claims; + + /// Get payment method by id. + /// + /// GetPaymentMethodById - GET /v71/paymentMethods/{id} + async fn get_payment_method_by_id( + &self, + event: &mut super::event::Event, + method: Method, + host: Host, + cookies: CookieJar, + claims: Self::Claims, + path_params: models::GetPaymentMethodByIdPathParams, + ) -> Result; + + /// Get payment methods. + /// + /// GetPaymentMethods - GET /v71/paymentMethods + async fn get_payment_methods( + &self, + event: &mut super::event::Event, + method: Method, + host: Host, + cookies: CookieJar, + claims: Self::Claims, + ) -> Result; + + /// Make a payment. + /// + /// PostMakePayment - POST /v71/payments + async fn post_make_payment( + &self, + event: &mut super::event::Event, + method: Method, + host: Host, + cookies: CookieJar, + claims: Self::Claims, + body: Option, + ) -> Result; +} diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/src/header.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/header.rs new file mode 100644 index 00000000000..970a99da88e --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/header.rs @@ -0,0 +1,188 @@ +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!( + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) + )), + }, + Err(e) => Err(format!( + r#"Unable to parse header {hdr_value:?} as a string - {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!( + r#"Unable to parse header: {hdr_value:?} as a string - {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!( + r#"Unable to convert {hdr_value:?} into a header - {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!(r#"Unable to convert header {hdr_value:?} to {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!( + r#"Unable to convert {hdr_value:?} from a header {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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), + }, + Err(e) => Err(format!( + r#"Unable to convert {hdr_value:?} from a header {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!( + r#"Unable to convert: {hdr_value:?} into a header: {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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), + }, + Err(e) => Err(format!( + r#"Unable to convert header {hdr_value:?} to string {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!( + r#"Unable to convert {hdr_value:?} to a header: {e}"# + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/src/lib.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/lib.rs new file mode 100644 index 00000000000..d461afebe02 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/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 = "/v71"; +pub const API_VERSION: &str = "1.0.0"; + +#[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/apikey-authorization/src/models.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs new file mode 100644 index 00000000000..b20237912cd --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs @@ -0,0 +1,891 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GetPaymentMethodByIdPathParams { + /// Id of the payment method + pub id: String, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Amount { + /// The three-character [ISO currency code](https://docs.adyen.com/development-resources/currency-codes). + #[serde(rename = "currency")] + #[validate(length(min = 3, max = 3), custom(function = "check_xss_string"))] + pub currency: String, + + /// The amount of the transaction, in [minor units](https://docs.adyen.com/development-resources/currency-codes). + #[serde(rename = "value")] + pub value: i64, +} + +impl Amount { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(currency: String, value: i64) -> Amount { + Amount { currency, value } + } +} + +/// Converts the Amount 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::fmt::Display for Amount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + Some("currency".to_string()), + Some(self.currency.to_string()), + Some("value".to_string()), + Some(self.value.to_string()), + ]; + + write!( + f, + "{}", + params.into_iter().flatten().collect::>().join(",") + ) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Amount value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Amount { + 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 currency: Vec, + 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 Amount".to_string(), + ); + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "currency" => intermediate_rep.currency.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[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 Amount".to_string(), + ); + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Amount { + currency: intermediate_rep + .currency + .into_iter() + .next() + .ok_or_else(|| "currency missing in Amount".to_string())?, + value: intermediate_rep + .value + .into_iter() + .next() + .ok_or_else(|| "value missing in Amount".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!( + r#"Invalid header value for Amount - value: {hdr_value} is invalid {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!( + r#"Unable to convert header value '{value}' into Amount - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct CheckoutError { + /// Error code + #[serde(rename = "code")] + #[validate(custom(function = "check_xss_string"))] + pub code: String, + + /// User-friendly message + #[serde(rename = "message")] + #[validate(custom(function = "check_xss_string"))] + pub message: String, +} + +impl CheckoutError { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(code: String, message: String) -> CheckoutError { + CheckoutError { code, message } + } +} + +/// Converts the CheckoutError 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::fmt::Display for CheckoutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + Some("code".to_string()), + Some(self.code.to_string()), + Some("message".to_string()), + Some(self.message.to_string()), + ]; + + write!( + f, + "{}", + params.into_iter().flatten().collect::>().join(",") + ) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a CheckoutError value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for CheckoutError { + 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 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 CheckoutError".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)] + "message" => intermediate_rep.message.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing CheckoutError".to_string(), + ); + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(CheckoutError { + code: intermediate_rep + .code + .into_iter() + .next() + .ok_or_else(|| "code missing in CheckoutError".to_string())?, + message: intermediate_rep + .message + .into_iter() + .next() + .ok_or_else(|| "message missing in CheckoutError".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!( + r#"Invalid header value for CheckoutError - value: {hdr_value} is invalid {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!( + r#"Unable to convert header value '{value}' into CheckoutError - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Payment { + #[serde(rename = "paymentMethod")] + #[validate(nested)] + pub payment_method: models::PaymentMethod, + + #[serde(rename = "amount")] + #[validate(nested)] + pub amount: models::Amount, + + #[serde(rename = "merchantAccount")] + #[validate(custom(function = "check_xss_string"))] + pub merchant_account: String, + + #[serde(rename = "reference")] + #[validate(custom(function = "check_xss_string"))] + #[serde(skip_serializing_if = "Option::is_none")] + pub reference: Option, + + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "channel")] + #[validate(custom(function = "check_xss_string"))] + #[serde(skip_serializing_if = "Option::is_none")] + pub channel: Option, +} + +impl Payment { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new( + payment_method: models::PaymentMethod, + amount: models::Amount, + merchant_account: String, + ) -> Payment { + Payment { + payment_method, + amount, + merchant_account, + reference: None, + channel: None, + } + } +} + +/// Converts the Payment 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::fmt::Display for Payment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping paymentMethod in query parameter serialization + + // Skipping amount in query parameter serialization + Some("merchantAccount".to_string()), + Some(self.merchant_account.to_string()), + self.reference + .as_ref() + .map(|reference| ["reference".to_string(), reference.to_string()].join(",")), + self.channel + .as_ref() + .map(|channel| ["channel".to_string(), channel.to_string()].join(",")), + ]; + + write!( + f, + "{}", + params.into_iter().flatten().collect::>().join(",") + ) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Payment value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Payment { + 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 payment_method: Vec, + pub amount: Vec, + pub merchant_account: Vec, + pub reference: Vec, + pub channel: 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 Payment".to_string(), + ); + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "paymentMethod" => intermediate_rep.payment_method.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "amount" => intermediate_rep.amount.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "merchantAccount" => intermediate_rep.merchant_account.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "reference" => intermediate_rep.reference.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "channel" => intermediate_rep.channel.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Payment".to_string(), + ); + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Payment { + payment_method: intermediate_rep + .payment_method + .into_iter() + .next() + .ok_or_else(|| "paymentMethod missing in Payment".to_string())?, + amount: intermediate_rep + .amount + .into_iter() + .next() + .ok_or_else(|| "amount missing in Payment".to_string())?, + merchant_account: intermediate_rep + .merchant_account + .into_iter() + .next() + .ok_or_else(|| "merchantAccount missing in Payment".to_string())?, + reference: intermediate_rep.reference.into_iter().next(), + channel: intermediate_rep.channel.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!( + r#"Invalid header value for Payment - value: {hdr_value} is invalid {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!( + r#"Unable to convert header value '{value}' into Payment - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct PaymentMethod { + /// Name of the payment method + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Type of the payment method + #[serde(rename = "type")] + #[validate(custom(function = "check_xss_string"))] + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, +} + +impl PaymentMethod { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> PaymentMethod { + PaymentMethod { + name: None, + r#type: None, + } + } +} + +/// Converts the PaymentMethod 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::fmt::Display for PaymentMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + self.r#type + .as_ref() + .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")), + ]; + + write!( + f, + "{}", + params.into_iter().flatten().collect::>().join(",") + ) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a PaymentMethod value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for PaymentMethod { + 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 r#type: 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 PaymentMethod".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)] + "type" => intermediate_rep.r#type.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing PaymentMethod".to_string(), + ); + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(PaymentMethod { + name: intermediate_rep.name.into_iter().next(), + r#type: intermediate_rep.r#type.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!( + r#"Invalid header value for PaymentMethod - value: {hdr_value} is invalid {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!( + r#"Unable to convert header value '{value}' into PaymentMethod - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct PaymentResult { + /// PSP ref + #[serde(rename = "pspReference")] + #[validate(custom(function = "check_xss_string"))] + pub psp_reference: String, + + /// Result code + /// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "resultCode")] + #[validate(custom(function = "check_xss_string"))] + pub result_code: String, +} + +impl PaymentResult { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new(psp_reference: String, result_code: String) -> PaymentResult { + PaymentResult { + psp_reference, + result_code, + } + } +} + +/// Converts the PaymentResult 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::fmt::Display for PaymentResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + Some("pspReference".to_string()), + Some(self.psp_reference.to_string()), + Some("resultCode".to_string()), + Some(self.result_code.to_string()), + ]; + + write!( + f, + "{}", + params.into_iter().flatten().collect::>().join(",") + ) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a PaymentResult value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for PaymentResult { + 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 psp_reference: Vec, + pub result_code: 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 PaymentResult".to_string(), + ); + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "pspReference" => intermediate_rep.psp_reference.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "resultCode" => intermediate_rep.result_code.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing PaymentResult".to_string(), + ); + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(PaymentResult { + psp_reference: intermediate_rep + .psp_reference + .into_iter() + .next() + .ok_or_else(|| "pspReference missing in PaymentResult".to_string())?, + result_code: intermediate_rep + .result_code + .into_iter() + .next() + .ok_or_else(|| "resultCode missing in PaymentResult".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!( + r#"Invalid header value for PaymentResult - value: {hdr_value} is invalid {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!( + r#"Unable to convert header value '{value}' into PaymentResult - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), + } + } +} diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/src/server/mod.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/server/mod.rs new file mode 100644 index 00000000000..9e6e975a096 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/server/mod.rs @@ -0,0 +1,545 @@ +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::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; +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::EventDispatcher + + apis::payments::Payments + + apis::payments::PaymentsAuthorization + + apis::ApiAuthBasic + + apis::ApiAuthBasic + + apis::ApiKeyAuthHeader + + apis::CookieAuthentication + + Send + + Sync + + 'static, + E: std::fmt::Debug + Send + Sync + 'static, + C: Send + Sync + 'static, +{ + // build our application with a route + Router::new() + .route( + "/v71/paymentMethods", + get(get_payment_methods::), + ) + .route( + "/v71/paymentMethods/{id}", + get(get_payment_method_by_id::), + ) + .route("/v71/payments", post(post_make_payment::)) + .with_state(api_impl) +} + +#[tracing::instrument(skip_all)] +fn get_payment_method_by_id_validation( + path_params: models::GetPaymentMethodByIdPathParams, +) -> std::result::Result<(models::GetPaymentMethodByIdPathParams,), ValidationErrors> { + path_params.validate()?; + + Ok((path_params,)) +} +/// GetPaymentMethodById - GET /v71/paymentMethods/{id} +#[tracing::instrument(skip_all)] +async fn get_payment_method_by_id( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::EventDispatcher + + apis::payments::Payments + + apis::payments::PaymentsAuthorization + + apis::ApiAuthBasic + + Send + + Sync, + E: std::fmt::Debug + Send + Sync + 'static, +{ + let start_at = chrono::Utc::now(); + + // Authentication + let claims_in_auth_header = api_impl + .as_ref() + .extract_claims_from_auth_header(apis::BasicAuthKind::Bearer, &headers, "authorization") + .await; + let claims = None.or(claims_in_auth_header); + let Some(claims) = claims else { + return response_with_status_code_only(StatusCode::UNAUTHORIZED); + }; + + #[allow(clippy::redundant_closure)] + let validation = + tokio::task::spawn_blocking(move || get_payment_method_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); + }; + + // Authorization + let authorization = api_impl + .as_ref() + .get_payment_method_by_id_authorize(&method, &host, &cookies, &claims, &path_params) + .await; + match authorization { + Ok(authorization) => match authorization { + apis::Authorization::Authorized => {} + apis::Authorization::Forbidden => { + return response_with_status_code_only(StatusCode::FORBIDDEN); + } + }, + Err(_) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let mut event = apis::event::Event::default(); + let result = api_impl + .as_ref() + .get_payment_method_by_id(&mut event, method, host, cookies, claims, path_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::payments::GetPaymentMethodByIdResponse::Status200_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)) + } + apis::payments::GetPaymentMethodByIdResponse::Status422_UnprocessableEntity(body) => { + let mut response = response.status(422); + { + 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(_why) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + if let Ok(resp) = resp.as_ref() + && !event.is_empty() + { + event.insert( + apis::event::convention::EVENT_TIMESTAMP.to_string(), + format!("{start_at:?}"), + ); + event.insert( + apis::event::convention::EVENT_SERVICE.to_string(), + api_impl.as_ref().service_name(), + ); + event.insert( + apis::event::convention::EVENT_STATUS_CODE.to_string(), + resp.status().as_u16().to_string(), + ); + event.insert( + apis::event::convention::EVENT_ACTION.to_string(), + "get_payment_method_by_id".to_string(), + ); + event.insert( + apis::event::convention::EVENT_LATENCY_SECS.to_string(), + format!( + "{:.6}", + chrono::Utc::now() + .signed_duration_since(start_at) + .as_seconds_f64() + ), + ); + api_impl.as_ref().dispatch(event).await; + } + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[tracing::instrument(skip_all)] +fn get_payment_methods_validation() -> std::result::Result<(), ValidationErrors> { + Ok(()) +} +/// GetPaymentMethods - GET /v71/paymentMethods +#[tracing::instrument(skip_all)] +async fn get_payment_methods( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::EventDispatcher + + apis::payments::Payments + + apis::payments::PaymentsAuthorization + + apis::ApiAuthBasic + + Send + + Sync, + E: std::fmt::Debug + Send + Sync + 'static, +{ + let start_at = chrono::Utc::now(); + + // Authentication + let claims_in_auth_header = api_impl + .as_ref() + .extract_claims_from_auth_header(apis::BasicAuthKind::Bearer, &headers, "authorization") + .await; + let claims = None.or(claims_in_auth_header); + let Some(claims) = claims else { + return response_with_status_code_only(StatusCode::UNAUTHORIZED); + }; + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || get_payment_methods_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); + }; + + // Authorization + let authorization = api_impl + .as_ref() + .get_payment_methods_authorize(&method, &host, &cookies, &claims) + .await; + match authorization { + Ok(authorization) => match authorization { + apis::Authorization::Authorized => {} + apis::Authorization::Forbidden => { + return response_with_status_code_only(StatusCode::FORBIDDEN); + } + }, + Err(_) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let mut event = apis::event::Event::default(); + let result = api_impl + .as_ref() + .get_payment_methods(&mut event, method, host, cookies, claims) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::payments::GetPaymentMethodsResponse::Status200_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(_why) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + if let Ok(resp) = resp.as_ref() + && !event.is_empty() + { + event.insert( + apis::event::convention::EVENT_TIMESTAMP.to_string(), + format!("{start_at:?}"), + ); + event.insert( + apis::event::convention::EVENT_SERVICE.to_string(), + api_impl.as_ref().service_name(), + ); + event.insert( + apis::event::convention::EVENT_STATUS_CODE.to_string(), + resp.status().as_u16().to_string(), + ); + event.insert( + apis::event::convention::EVENT_ACTION.to_string(), + "get_payment_methods".to_string(), + ); + event.insert( + apis::event::convention::EVENT_LATENCY_SECS.to_string(), + format!( + "{:.6}", + chrono::Utc::now() + .signed_duration_since(start_at) + .as_seconds_f64() + ), + ); + api_impl.as_ref().dispatch(event).await; + } + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[derive(validator::Validate)] +#[allow(dead_code)] +struct PostMakePaymentBodyValidator<'a> { + #[validate(nested)] + body: &'a models::Payment, +} + +#[tracing::instrument(skip_all)] +fn post_make_payment_validation( + body: Option, +) -> std::result::Result<(Option,), ValidationErrors> { + if let Some(body) = &body { + let b = PostMakePaymentBodyValidator { body }; + b.validate()?; + } + + Ok((body,)) +} +/// PostMakePayment - POST /v71/payments +#[tracing::instrument(skip_all)] +async fn post_make_payment( + method: Method, + host: Host, + cookies: CookieJar, + headers: HeaderMap, + State(api_impl): State, + Json(body): Json>, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::EventDispatcher + + apis::payments::Payments + + apis::payments::PaymentsAuthorization + + apis::CookieAuthentication + + apis::ApiAuthBasic + + Send + + Sync, + E: std::fmt::Debug + Send + Sync + 'static, +{ + let start_at = chrono::Utc::now(); + + // Authentication + let claims_in_cookie = api_impl + .as_ref() + .extract_claims_from_cookie(&cookies, "X-API-Key") + .await; + let claims_in_auth_header = api_impl + .as_ref() + .extract_claims_from_auth_header(apis::BasicAuthKind::Bearer, &headers, "authorization") + .await; + let claims = None.or(claims_in_cookie).or(claims_in_auth_header); + let Some(claims) = claims else { + return response_with_status_code_only(StatusCode::UNAUTHORIZED); + }; + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || post_make_payment_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); + }; + + // Authorization + let authorization = api_impl + .as_ref() + .post_make_payment_authorize(&method, &host, &cookies, &claims, &body) + .await; + match authorization { + Ok(authorization) => match authorization { + apis::Authorization::Authorized => {} + apis::Authorization::Forbidden => { + return response_with_status_code_only(StatusCode::FORBIDDEN); + } + }, + Err(_) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let mut event = apis::event::Event::default(); + let result = api_impl + .as_ref() + .post_make_payment(&mut event, method, host, cookies, claims, body) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::payments::PostMakePaymentResponse::Status200_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)) + } + apis::payments::PostMakePaymentResponse::Status422_UnprocessableEntity(body) => { + let mut response = response.status(422); + { + 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(_why) => { + return response_with_status_code_only(StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + if let Ok(resp) = resp.as_ref() + && !event.is_empty() + { + event.insert( + apis::event::convention::EVENT_TIMESTAMP.to_string(), + format!("{start_at:?}"), + ); + event.insert( + apis::event::convention::EVENT_SERVICE.to_string(), + api_impl.as_ref().service_name(), + ); + event.insert( + apis::event::convention::EVENT_STATUS_CODE.to_string(), + resp.status().as_u16().to_string(), + ); + event.insert( + apis::event::convention::EVENT_ACTION.to_string(), + "post_make_payment".to_string(), + ); + event.insert( + apis::event::convention::EVENT_LATENCY_SECS.to_string(), + format!( + "{:.6}", + chrono::Utc::now() + .signed_duration_since(start_at) + .as_seconds_f64() + ), + ); + api_impl.as_ref().dispatch(event).await; + } + + resp.map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/src/types.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/types.rs new file mode 100644 index 00000000000..d99febd86d5 --- /dev/null +++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/types.rs @@ -0,0 +1,790 @@ +use std::{mem, str::FromStr}; + +use base64::{Engine, engine::general_purpose}; +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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```should_panic + /// # use apikey_authorization::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 apikey_authorization::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```should_panic + /// # use apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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 apikey_authorization::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/apikey-auths/.openapi-generator/VERSION b/samples/server/petstore/rust-axum/output/apikey-auths/.openapi-generator/VERSION index 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 b297be95f69..5bff55119b2 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml @@ -3,7 +3,7 @@ name = "apikey-auths" version = "1.0.0" authors = ["OpenAPI Generator team and contributors"] description = "Checkout Basic" -edition = "2021" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 103421c42aa..16979debcbd 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/apis/payments.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/apis/payments.rs index 0db9281e021..2bdf4d433d2 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/src/apis/payments.rs +++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/apis/payments.rs @@ -48,6 +48,7 @@ pub trait Payments: /// GetPaymentMethodById - GET /v71/paymentMethods/{id} async fn get_payment_method_by_id( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -60,6 +61,7 @@ pub trait Payments: /// GetPaymentMethods - GET /v71/paymentMethods async fn get_payment_methods( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -71,6 +73,7 @@ pub trait Payments: /// PostMakePayment - POST /v71/payments async fn post_make_payment( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/header.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/header.rs index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/src/header.rs +++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs index e85e099e305..b20237912cd 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs +++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct GetPaymentMethodByIdPathParams { @@ -19,7 +73,7 @@ pub struct GetPaymentMethodByIdPathParams { pub struct Amount { /// The three-character [ISO currency code](https://docs.adyen.com/development-resources/currency-codes). #[serde(rename = "currency")] - #[validate(length(min = 3, max = 3))] + #[validate(length(min = 3, max = 3), custom(function = "check_xss_string"))] pub currency: String, /// The amount of the transaction, in [minor units](https://docs.adyen.com/development-resources/currency-codes). @@ -81,7 +135,7 @@ impl std::str::FromStr for Amount { None => { return std::result::Result::Err( "Missing value while parsing Amount".to_string(), - ) + ); } }; @@ -99,7 +153,7 @@ impl std::str::FromStr for Amount { _ => { return std::result::Result::Err( "Unexpected key while parsing Amount".to_string(), - ) + ); } } } @@ -137,8 +191,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 Amount - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for Amount - value: {hdr_value} is invalid {e}"# )), } } @@ -156,14 +209,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into Amount - {}", - value, err + r#"Unable to convert header value '{value}' into Amount - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -174,10 +225,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { pub struct CheckoutError { /// Error code #[serde(rename = "code")] + #[validate(custom(function = "check_xss_string"))] pub code: String, /// User-friendly message #[serde(rename = "message")] + #[validate(custom(function = "check_xss_string"))] pub message: String, } @@ -235,7 +288,7 @@ impl std::str::FromStr for CheckoutError { None => { return std::result::Result::Err( "Missing value while parsing CheckoutError".to_string(), - ) + ); } }; @@ -253,7 +306,7 @@ impl std::str::FromStr for CheckoutError { _ => { return std::result::Result::Err( "Unexpected key while parsing CheckoutError".to_string(), - ) + ); } } } @@ -291,8 +344,7 @@ impl std::convert::TryFrom> for HeaderVal 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 CheckoutError - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for CheckoutError - value: {hdr_value} is invalid {e}"# )), } } @@ -310,14 +362,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into CheckoutError - {}", - value, err + r#"Unable to convert header value '{value}' into CheckoutError - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -327,20 +377,25 @@ impl std::convert::TryFrom for header::IntoHeaderValue, /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "channel")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub channel: Option, } @@ -419,7 +474,7 @@ impl std::str::FromStr for Payment { None => { return std::result::Result::Err( "Missing value while parsing Payment".to_string(), - ) + ); } }; @@ -451,7 +506,7 @@ impl std::str::FromStr for Payment { _ => { return std::result::Result::Err( "Unexpected key while parsing Payment".to_string(), - ) + ); } } } @@ -496,8 +551,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 Payment - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for Payment - value: {hdr_value} is invalid {e}"# )), } } @@ -515,14 +569,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into Payment - {}", - value, err + r#"Unable to convert header value '{value}' into Payment - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -534,11 +586,13 @@ pub struct PaymentMethod { /// Name of the payment method /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// Type of the payment method #[serde(rename = "type")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub r#type: Option, } @@ -602,7 +656,7 @@ impl std::str::FromStr for PaymentMethod { None => { return std::result::Result::Err( "Missing value while parsing PaymentMethod".to_string(), - ) + ); } }; @@ -620,7 +674,7 @@ impl std::str::FromStr for PaymentMethod { _ => { return std::result::Result::Err( "Unexpected key while parsing PaymentMethod".to_string(), - ) + ); } } } @@ -650,8 +704,7 @@ impl std::convert::TryFrom> for HeaderVal 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 PaymentMethod - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for PaymentMethod - value: {hdr_value} is invalid {e}"# )), } } @@ -669,14 +722,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into PaymentMethod - {}", - value, err + r#"Unable to convert header value '{value}' into PaymentMethod - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -687,11 +738,13 @@ impl std::convert::TryFrom for header::IntoHeaderValue { return std::result::Result::Err( "Missing value while parsing PaymentResult".to_string(), - ) + ); } }; @@ -770,7 +823,7 @@ impl std::str::FromStr for PaymentResult { _ => { return std::result::Result::Err( "Unexpected key while parsing PaymentResult".to_string(), - ) + ); } } } @@ -808,8 +861,7 @@ impl std::convert::TryFrom> for HeaderVal 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 PaymentResult - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for PaymentResult - value: {hdr_value} is invalid {e}"# )), } } @@ -827,14 +879,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into PaymentResult - {}", - value, err + r#"Unable to convert header value '{value}' into PaymentResult - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } 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 bb5c2f32d6a..f7808f3567b 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -71,10 +71,7 @@ where .await; let claims = None.or(claims_in_auth_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -187,10 +184,7 @@ where .await; let claims = None.or(claims_in_auth_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -302,10 +296,7 @@ where .await; let claims = None.or(claims_in_cookie).or(claims_in_auth_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -391,3 +382,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/types.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/types.rs index 6086b856b4a..fd55b5e9a0f 100644 --- a/samples/server/petstore/rust-axum/output/apikey-auths/src/types.rs +++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 0eff592c86e..c136545e05e 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml @@ -3,7 +3,7 @@ name = "multipart-v3" version = "1.0.7" authors = ["OpenAPI Generator team and contributors"] description = "API under test" -edition = "2021" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 7ab667b4065..2481a15e880 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/multipart-v3/src/apis/default.rs b/samples/server/petstore/rust-axum/output/multipart-v3/src/apis/default.rs index 1ef5b886669..b9cdb2c1dfd 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/apis/default.rs @@ -38,6 +38,7 @@ pub trait Default: super::Error /// MultipartRelatedRequestPost - POST /multipart_related_request async fn multipart_related_request_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -47,6 +48,7 @@ pub trait Default: super::Error /// MultipartRequestPost - POST /multipart_request async fn multipart_request_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -56,6 +58,7 @@ pub trait Default: super::Error /// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types async fn multiple_identical_mime_types_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 index e82cb7dda57..7b10d2066db 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/models.rs @@ -7,10 +7,65 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct MultipartRelatedRequest { #[serde(rename = "object_field")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub object_field: Option, @@ -85,7 +140,7 @@ impl std::str::FromStr for MultipartRelatedRequest { None => { return std::result::Result::Err( "Missing value while parsing MultipartRelatedRequest".to_string(), - ) + ); } }; @@ -132,8 +187,7 @@ impl std::convert::TryFrom> for 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 + r#"Invalid header value for MultipartRelatedRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -151,14 +205,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into MultipartRelatedRequest - {}", - value, err + r#"Unable to convert header value '{value}' into MultipartRelatedRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -168,13 +220,16 @@ impl std::convert::TryFrom for header::IntoHeaderValue, #[serde(rename = "object_field")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub object_field: Option, @@ -254,7 +309,7 @@ impl std::str::FromStr for MultipartRequest { None => { return std::result::Result::Err( "Missing value while parsing MultipartRequest".to_string(), - ) + ); } }; @@ -281,7 +336,7 @@ impl std::str::FromStr for MultipartRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing MultipartRequest".to_string(), - ) + ); } } } @@ -321,8 +376,7 @@ impl std::convert::TryFrom> for Header 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 MultipartRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for MultipartRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -340,14 +394,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into MultipartRequest - {}", - value, err + r#"Unable to convert header value '{value}' into MultipartRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -357,9 +409,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue>, } @@ -430,7 +484,7 @@ impl std::str::FromStr for MultipartRequestObjectField { None => { return std::result::Result::Err( "Missing value while parsing MultipartRequestObjectField".to_string(), - ) + ); } }; @@ -473,8 +527,7 @@ impl std::convert::TryFrom> 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 + r#"Invalid header value for MultipartRequestObjectField - value: {hdr_value} is invalid {e}"# )), } } @@ -492,14 +545,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into MultipartRequestObjectField - {}", - value, err + r#"Unable to convert header value '{value}' into MultipartRequestObjectField - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -577,7 +628,7 @@ impl std::str::FromStr for MultipleIdenticalMimeTypesPostRequest { return std::result::Result::Err( "Missing value while parsing MultipleIdenticalMimeTypesPostRequest" .to_string(), - ) + ); } }; @@ -615,10 +666,10 @@ impl std::convert::TryFrom 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)) + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Invalid header value for MultipleIdenticalMimeTypesPostRequest - value: {hdr_value} is invalid {e}"# + )), } } } @@ -631,17 +682,20 @@ impl std::convert::TryFrom 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::Ok(value) => { + match ::from_str(value) + { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) } - }, - std::result::Result::Err(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(err) => std::result::Result::Err(format!( + r#"Unable to convert header value '{value}' into MultipleIdenticalMimeTypesPostRequest - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {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 index 76855409c9a..c052383f748 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -217,3 +217,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index e2065a121c3..63ebd9d6833 100644 --- a/samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs +++ b/samples/server/petstore/rust-axum/output/multipart-v3/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 f5733d3a5f0..2d1f7bd5612 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/openapi-v3/Cargo.toml @@ -3,7 +3,7 @@ name = "openapi-v3" version = "1.0.7" authors = ["OpenAPI Generator team and contributors"] description = "API under test" -edition = "2021" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 6ad58536831..538dc2574b5 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/default.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/default.rs index 65b74d4a39c..99f8d44cbe1 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/default.rs @@ -43,6 +43,14 @@ pub enum EnumInPathPathParamGetResponse { Status200_Success, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum ExamplesTestResponse { + /// OK + Status200_OK(models::AdditionalPropertiesReferencedAnyOfObject), +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[must_use] #[allow(clippy::large_enum_variant)] @@ -278,6 +286,7 @@ pub trait Default: super::Error /// AnyOfGet - GET /any-of async fn any_of_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -287,6 +296,7 @@ pub trait Default: super::Error /// CallbackWithHeaderPost - POST /callback-with-header async fn callback_with_header_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -296,6 +306,7 @@ pub trait Default: super::Error /// ComplexQueryParamGet - GET /complex-query-param async fn complex_query_param_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -305,17 +316,31 @@ pub trait Default: super::Error /// 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; + /// Test examples. + /// + /// ExamplesTest - GET /examples-test + async fn examples_test( + &self, + + method: &Method, + host: &Host, + cookies: &CookieJar, + query_params: &models::ExamplesTestQueryParams, + ) -> Result; + /// Test a Form Post. /// /// FormTest - POST /form-test async fn form_test( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -325,6 +350,7 @@ pub trait Default: super::Error /// GetWithBooleanParameter - GET /get-with-bool async fn get_with_boolean_parameter( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -334,6 +360,7 @@ pub trait Default: super::Error /// JsonComplexQueryParamGet - GET /json-complex-query-param async fn json_complex_query_param_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -343,6 +370,7 @@ pub trait Default: super::Error /// MandatoryRequestHeaderGet - GET /mandatory-request-header async fn mandatory_request_header_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -352,6 +380,7 @@ pub trait Default: super::Error /// MergePatchJsonGet - GET /merge-patch-json async fn merge_patch_json_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -362,6 +391,7 @@ pub trait Default: super::Error /// MultigetGet - GET /multiget async fn multiget_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -370,6 +400,7 @@ pub trait Default: super::Error /// MultipleAuthSchemeGet - GET /multiple_auth_scheme async fn multiple_auth_scheme_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -378,6 +409,7 @@ pub trait Default: super::Error /// MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet - GET /multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b} async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -387,6 +419,7 @@ pub trait Default: super::Error /// OneOfGet - GET /one-of async fn one_of_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -395,6 +428,7 @@ pub trait Default: super::Error /// OverrideServerGet - GET /override-server async fn override_server_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -405,6 +439,7 @@ pub trait Default: super::Error /// ParamgetGet - GET /paramget async fn paramget_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -414,6 +449,7 @@ pub trait Default: super::Error /// ReadonlyAuthSchemeGet - GET /readonly_auth_scheme async fn readonly_auth_scheme_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -422,6 +458,7 @@ pub trait Default: super::Error /// RegisterCallbackPost - POST /register-callback async fn register_callback_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -431,6 +468,7 @@ pub trait Default: super::Error /// RequiredOctetStreamPut - PUT /required_octet_stream async fn required_octet_stream_put( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -440,6 +478,7 @@ pub trait Default: super::Error /// ResponsesWithHeadersGet - GET /responses_with_headers async fn responses_with_headers_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -448,6 +487,7 @@ pub trait Default: super::Error /// Rfc7807Get - GET /rfc7807 async fn rfc7807_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -456,6 +496,7 @@ pub trait Default: super::Error /// TwoFirstLetterHeaders - POST /operation-two-first-letter-headers async fn two_first_letter_headers( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -465,6 +506,7 @@ pub trait Default: super::Error /// UntypedPropertyGet - GET /untyped_property async fn untyped_property_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -474,6 +516,7 @@ pub trait Default: super::Error /// UuidGet - GET /uuid async fn uuid_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -482,6 +525,7 @@ pub trait Default: super::Error /// XmlExtraPost - POST /xml_extra async fn xml_extra_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -491,6 +535,7 @@ pub trait Default: super::Error /// XmlOtherPost - POST /xml_other async fn xml_other_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -500,6 +545,7 @@ pub trait Default: super::Error /// XmlOtherPut - PUT /xml_other async fn xml_other_put( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -511,6 +557,7 @@ pub trait Default: super::Error /// XmlPost - POST /xml async fn xml_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -520,6 +567,7 @@ pub trait Default: super::Error /// XmlPut - PUT /xml async fn xml_put( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/info_repo.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/info_repo.rs index 822576c5346..d4ea2e89cb4 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/info_repo.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/info_repo.rs @@ -24,6 +24,7 @@ pub trait InfoRepo: /// GetRepoInfo - GET /repos/{repoId} async fn get_repo_info( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/repo.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/repo.rs index f3c5c204493..7f454233599 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/repo.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/apis/repo.rs @@ -22,6 +22,7 @@ pub trait Repo: super::ErrorHan /// CreateRepo - POST /repos async fn create_repo( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 a45adbcbb02..4d2fb31b5ab 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 @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct AnyOfGetQueryParams { @@ -38,6 +92,15 @@ 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 ExamplesTestQueryParams { + /// A list of IDs to get + #[serde(rename = "ids")] + #[serde(default)] + pub ids: Vec, +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct GetWithBooleanParameterQueryParams { @@ -163,7 +226,9 @@ impl ::std::str::FromStr for AdditionalPropertiesReferencedAnyOfObject { type Err = &'static str; fn from_str(s: &str) -> std::result::Result { - std::result::Result::Err("Parsing additionalProperties for AdditionalPropertiesReferencedAnyOfObject is not supported") + std::result::Result::Err( + "Parsing additionalProperties for AdditionalPropertiesReferencedAnyOfObject is not supported", + ) } } @@ -239,6 +304,7 @@ pub struct AdditionalPropertiesWithNullable { pub nullable_string: Option>, #[serde(rename = "nullableMap")] + #[validate(custom(function = "check_xss_map_nested"))] #[serde(skip_serializing_if = "Option::is_none")] pub nullable_map: Option>, } @@ -306,7 +372,7 @@ impl std::str::FromStr for AdditionalPropertiesWithNullable { None => { return std::result::Result::Err( "Missing value while parsing AdditionalPropertiesWithNullable".to_string(), - ) + ); } }; @@ -346,10 +412,10 @@ impl std::convert::TryFrom 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 AdditionalPropertiesWithNullable - value: {} is invalid {}", - hdr_value, e)) + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Invalid header value for AdditionalPropertiesWithNullable - value: {hdr_value} is invalid {e}"# + )), } } } @@ -362,17 +428,19 @@ impl std::convert::TryFrom 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 AdditionalPropertiesWithNullable - {}", - value, err)) + 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(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(err) => std::result::Result::Err(format!( + r#"Unable to convert header value '{value}' into AdditionalPropertiesWithNullable - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), } } } @@ -489,8 +557,7 @@ impl std::convert::TryFrom> for HeaderV 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 + r#"Invalid header value for AnotherXmlArray - value: {hdr_value} is invalid {e}"# )), } } @@ -508,14 +575,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into AnotherXmlArray - {}", - value, err + r#"Unable to convert header value '{value}' into AnotherXmlArray - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -574,6 +639,7 @@ impl std::ops::DerefMut for AnotherXmlInner { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct AnotherXmlObject { #[serde(rename = "inner_string")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub inner_string: Option, } @@ -590,10 +656,10 @@ impl AnotherXmlObject { /// Should be implemented in a serde serializer impl std::fmt::Display for AnotherXmlObject { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .inner_string - .as_ref() - .map(|inner_string| ["inner_string".to_string(), inner_string.to_string()].join(","))]; + let params: Vec> = + vec![self.inner_string.as_ref().map(|inner_string| { + ["inner_string".to_string(), inner_string.to_string()].join(",") + })]; write!( f, @@ -629,7 +695,7 @@ impl std::str::FromStr for AnotherXmlObject { None => { return std::result::Result::Err( "Missing value while parsing AnotherXmlObject".to_string(), - ) + ); } }; @@ -643,7 +709,7 @@ impl std::str::FromStr for AnotherXmlObject { _ => { return std::result::Result::Err( "Unexpected key while parsing AnotherXmlObject".to_string(), - ) + ); } } } @@ -672,8 +738,7 @@ impl std::convert::TryFrom> for Header 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 + r#"Invalid header value for AnotherXmlObject - value: {hdr_value} is invalid {e}"# )), } } @@ -691,14 +756,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into AnotherXmlObject - {}", - value, err + r#"Unable to convert header value '{value}' into AnotherXmlObject - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -797,9 +860,11 @@ impl PartialEq for AnyOfObject { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct AnyOfProperty { #[serde(rename = "requiredAnyOf")] + #[validate(nested)] pub required_any_of: models::AnyOfObject, #[serde(rename = "optionalAnyOf")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub optional_any_of: Option, } @@ -861,7 +926,7 @@ impl std::str::FromStr for AnyOfProperty { None => { return std::result::Result::Err( "Missing value while parsing AnyOfProperty".to_string(), - ) + ); } }; @@ -881,7 +946,7 @@ impl std::str::FromStr for AnyOfProperty { _ => { return std::result::Result::Err( "Unexpected key while parsing AnyOfProperty".to_string(), - ) + ); } } } @@ -915,8 +980,7 @@ impl std::convert::TryFrom> for HeaderVal 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 + r#"Invalid header value for AnyOfProperty - value: {hdr_value} is invalid {e}"# )), } } @@ -934,14 +998,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into AnyOfProperty - {}", - value, err + r#"Unable to convert header value '{value}' into AnyOfProperty - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -952,10 +1014,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue, #[serde(rename = "inner_array")] + #[validate(nested)] pub inner_array: models::XmlArray, } @@ -1016,7 +1080,7 @@ impl std::str::FromStr for DuplicateXmlObject { None => { return std::result::Result::Err( "Missing value while parsing DuplicateXmlObject".to_string(), - ) + ); } }; @@ -1035,7 +1099,7 @@ impl std::str::FromStr for DuplicateXmlObject { _ => { return std::result::Result::Err( "Unexpected key while parsing DuplicateXmlObject".to_string(), - ) + ); } } } @@ -1069,8 +1133,7 @@ impl std::convert::TryFrom> for Head 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 + r#"Invalid header value for DuplicateXmlObject - value: {hdr_value} is invalid {e}"# )), } } @@ -1088,14 +1151,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into DuplicateXmlObject - {}", - value, err + r#"Unable to convert header value '{value}' into DuplicateXmlObject - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1120,6 +1181,12 @@ pub enum EnumWithStarObject { Star, } +impl validator::Validate for EnumWithStarObject { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + impl std::fmt::Display for EnumWithStarObject { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { @@ -1138,7 +1205,7 @@ impl std::str::FromStr for EnumWithStarObject { "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)), + _ => std::result::Result::Err(format!(r#"Value not valid: {s}"#)), } } } @@ -1243,6 +1310,7 @@ impl std::ops::DerefMut for Error { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct FormTestRequest { #[serde(rename = "requiredArray")] + #[validate(custom(function = "check_xss_vec_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub required_array: Option>, } @@ -1308,7 +1376,7 @@ impl std::str::FromStr for FormTestRequest { None => { return std::result::Result::Err( "Missing value while parsing FormTestRequest".to_string(), - ) + ); } }; @@ -1319,12 +1387,12 @@ impl std::str::FromStr for FormTestRequest { return std::result::Result::Err( "Parsing a container in this style is not supported in FormTestRequest" .to_string(), - ) + ); } _ => { return std::result::Result::Err( "Unexpected key while parsing FormTestRequest".to_string(), - ) + ); } } } @@ -1353,8 +1421,7 @@ impl std::convert::TryFrom> for HeaderV 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 FormTestRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FormTestRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -1372,14 +1439,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FormTestRequest - {}", - value, err + r#"Unable to convert header value '{value}' into FormTestRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1418,6 +1483,7 @@ impl PartialEq for Model12345AnyOfObject { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct MultigetGet201Response { #[serde(rename = "foo")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub foo: Option, } @@ -1434,10 +1500,11 @@ impl MultigetGet201Response { /// Should be implemented in a serde serializer impl std::fmt::Display for MultigetGet201Response { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .foo - .as_ref() - .map(|foo| ["foo".to_string(), foo.to_string()].join(","))]; + let params: Vec> = vec![ + self.foo + .as_ref() + .map(|foo| ["foo".to_string(), foo.to_string()].join(",")), + ]; write!( f, @@ -1473,7 +1540,7 @@ impl std::str::FromStr for MultigetGet201Response { None => { return std::result::Result::Err( "Missing value while parsing MultigetGet201Response".to_string(), - ) + ); } }; @@ -1487,7 +1554,7 @@ impl std::str::FromStr for MultigetGet201Response { _ => { return std::result::Result::Err( "Unexpected key while parsing MultigetGet201Response".to_string(), - ) + ); } } } @@ -1516,8 +1583,7 @@ impl std::convert::TryFrom> for 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 + r#"Invalid header value for MultigetGet201Response - value: {hdr_value} is invalid {e}"# )), } } @@ -1535,14 +1601,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into MultigetGet201Response - {}", - value, err + r#"Unable to convert header value '{value}' into MultigetGet201Response - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1695,8 +1759,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for MyIdList - value: {hdr_value} is invalid {e}"# )), } } @@ -1714,14 +1777,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into MyIdList - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1970,7 +2031,7 @@ impl std::str::FromStr for NullableTest { None => { return std::result::Result::Err( "Missing value while parsing NullableTest".to_string(), - ) + ); } }; @@ -1997,30 +2058,30 @@ impl std::str::FromStr for NullableTest { 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(), - ) + ); } } } @@ -2066,8 +2127,7 @@ impl std::convert::TryFrom> for HeaderValu 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 + r#"Invalid header value for NullableTest - value: {hdr_value} is invalid {e}"# )), } } @@ -2085,14 +2145,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into NullableTest - {}", - value, err + r#"Unable to convert header value '{value}' into NullableTest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2173,7 +2231,7 @@ impl std::str::FromStr for ObjectHeader { None => { return std::result::Result::Err( "Missing value while parsing ObjectHeader".to_string(), - ) + ); } }; @@ -2191,7 +2249,7 @@ impl std::str::FromStr for ObjectHeader { _ => { return std::result::Result::Err( "Unexpected key while parsing ObjectHeader".to_string(), - ) + ); } } } @@ -2225,8 +2283,7 @@ impl std::convert::TryFrom> for HeaderValu 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 + r#"Invalid header value for ObjectHeader - value: {hdr_value} is invalid {e}"# )), } } @@ -2244,14 +2301,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ObjectHeader - {}", - value, err + r#"Unable to convert header value '{value}' into ObjectHeader - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2326,7 +2381,7 @@ impl std::str::FromStr for ObjectParam { None => { return std::result::Result::Err( "Missing value while parsing ObjectParam".to_string(), - ) + ); } }; @@ -2344,7 +2399,7 @@ impl std::str::FromStr for ObjectParam { _ => { return std::result::Result::Err( "Unexpected key while parsing ObjectParam".to_string(), - ) + ); } } } @@ -2378,8 +2433,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for ObjectParam - value: {hdr_value} is invalid {e}"# )), } } @@ -2397,14 +2451,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into ObjectParam - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2496,7 +2548,7 @@ impl std::str::FromStr for ObjectUntypedProps { None => { return std::result::Result::Err( "Missing value while parsing ObjectUntypedProps".to_string(), - ) + ); } }; @@ -2550,8 +2602,7 @@ impl std::convert::TryFrom> for Head 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 + r#"Invalid header value for ObjectUntypedProps - value: {hdr_value} is invalid {e}"# )), } } @@ -2569,14 +2620,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ObjectUntypedProps - {}", - value, err + r#"Unable to convert header value '{value}' into ObjectUntypedProps - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2586,6 +2635,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue>, } @@ -2648,7 +2698,7 @@ impl std::str::FromStr for ObjectWithArrayOfObjects { None => { return std::result::Result::Err( "Missing value while parsing ObjectWithArrayOfObjects".to_string(), - ) + ); } }; @@ -2684,8 +2734,7 @@ impl std::convert::TryFrom> fo 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 + r#"Invalid header value for ObjectWithArrayOfObjects - value: {hdr_value} is invalid {e}"# )), } } @@ -2703,14 +2752,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ObjectWithArrayOfObjects - {}", - value, err + r#"Unable to convert header value '{value}' into ObjectWithArrayOfObjects - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2937,6 +2984,12 @@ pub enum StringEnum { Bar, } +impl validator::Validate for StringEnum { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + impl std::fmt::Display for StringEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { @@ -2953,7 +3006,7 @@ impl std::str::FromStr for StringEnum { 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)), + _ => std::result::Result::Err(format!(r#"Value not valid: {s}"#)), } } } @@ -3154,8 +3207,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for XmlArray - value: {hdr_value} is invalid {e}"# )), } } @@ -3173,14 +3225,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into XmlArray - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3239,6 +3289,7 @@ impl std::ops::DerefMut for XmlInner { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct XmlObject { #[serde(rename = "innerString")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub inner_string: Option, @@ -3310,7 +3361,7 @@ impl std::str::FromStr for XmlObject { None => { return std::result::Result::Err( "Missing value while parsing XmlObject".to_string(), - ) + ); } }; @@ -3328,7 +3379,7 @@ impl std::str::FromStr for XmlObject { _ => { return std::result::Result::Err( "Unexpected key while parsing XmlObject".to_string(), - ) + ); } } } @@ -3358,8 +3409,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for XmlObject - value: {hdr_value} is invalid {e}"# )), } } @@ -3377,14 +3427,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into XmlObject - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {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 index 7eacaecabd5..728444c5414 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -38,6 +38,9 @@ where .route("/enum_in_path/{path_param}", get(enum_in_path_path_param_get::) ) + .route("/examples-test", + get(examples_test::) + ) .route("/form-test", post(form_test::) ) @@ -422,6 +425,86 @@ where }) } +#[tracing::instrument(skip_all)] +fn examples_test_validation( + query_params: models::ExamplesTestQueryParams, +) -> std::result::Result<(models::ExamplesTestQueryParams,), ValidationErrors> { + query_params.validate()?; + + Ok((query_params,)) +} +/// ExamplesTest - GET /examples-test +#[tracing::instrument(skip_all)] +async fn examples_test( + 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, +{ + let validation = examples_test_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() + .examples_test(&method, &host, &cookies, &query_params) + .await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::default::ExamplesTestResponse::Status200_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(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 + }) +} + #[derive(validator::Validate)] #[allow(dead_code)] struct FormTestBodyValidator<'a> { @@ -645,7 +728,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header X-Header - {}", err))) + .body(Body::from(format!("Invalid header X-Header - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -1016,9 +1099,9 @@ where }, }, 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; + // 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; }, }; @@ -1093,9 +1176,9 @@ where }, }, 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; + // 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; }, }; @@ -1355,9 +1438,9 @@ where }, }, 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; + // 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; }, }; @@ -1539,7 +1622,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling success_info header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1553,7 +1636,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling bool_header header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1569,7 +1652,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling object_header header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1611,7 +1694,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling further_info header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1627,7 +1710,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling failure_info header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1803,7 +1886,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header x-header-one - {}", err))) + .body(Body::from(format!("Invalid header x-header-one - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -1820,7 +1903,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header x-header-two - {}", err))) + .body(Body::from(format!("Invalid header x-header-two - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -1932,9 +2015,9 @@ where }, }, 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; + // 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; }, }; @@ -2511,3 +2594,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index ab6e8da11c7..c2331f6d31d 100644 --- a/samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs +++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 13e249edd96..5229ea5e9e7 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/ops-v3/Cargo.toml @@ -3,7 +3,7 @@ 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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 fbd7e5c7272..42a0575c8fd 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/ops-v3/src/apis/default.rs b/samples/server/petstore/rust-axum/output/ops-v3/src/apis/default.rs index f82fc8ea970..cbae47c7a87 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/apis/default.rs @@ -310,6 +310,7 @@ pub trait Default: super::Error /// Op10Get - GET /op10 async fn op10_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -318,6 +319,7 @@ pub trait Default: super::Error /// Op11Get - GET /op11 async fn op11_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -326,6 +328,7 @@ pub trait Default: super::Error /// Op12Get - GET /op12 async fn op12_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -334,6 +337,7 @@ pub trait Default: super::Error /// Op13Get - GET /op13 async fn op13_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -342,6 +346,7 @@ pub trait Default: super::Error /// Op14Get - GET /op14 async fn op14_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -350,6 +355,7 @@ pub trait Default: super::Error /// Op15Get - GET /op15 async fn op15_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -358,6 +364,7 @@ pub trait Default: super::Error /// Op16Get - GET /op16 async fn op16_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -366,6 +373,7 @@ pub trait Default: super::Error /// Op17Get - GET /op17 async fn op17_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -374,6 +382,7 @@ pub trait Default: super::Error /// Op18Get - GET /op18 async fn op18_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -382,6 +391,7 @@ pub trait Default: super::Error /// Op19Get - GET /op19 async fn op19_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -390,6 +400,7 @@ pub trait Default: super::Error /// Op1Get - GET /op1 async fn op1_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -398,6 +409,7 @@ pub trait Default: super::Error /// Op20Get - GET /op20 async fn op20_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -406,6 +418,7 @@ pub trait Default: super::Error /// Op21Get - GET /op21 async fn op21_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -414,6 +427,7 @@ pub trait Default: super::Error /// Op22Get - GET /op22 async fn op22_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -422,6 +436,7 @@ pub trait Default: super::Error /// Op23Get - GET /op23 async fn op23_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -430,6 +445,7 @@ pub trait Default: super::Error /// Op24Get - GET /op24 async fn op24_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -438,6 +454,7 @@ pub trait Default: super::Error /// Op25Get - GET /op25 async fn op25_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -446,6 +463,7 @@ pub trait Default: super::Error /// Op26Get - GET /op26 async fn op26_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -454,6 +472,7 @@ pub trait Default: super::Error /// Op27Get - GET /op27 async fn op27_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -462,6 +481,7 @@ pub trait Default: super::Error /// Op28Get - GET /op28 async fn op28_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -470,6 +490,7 @@ pub trait Default: super::Error /// Op29Get - GET /op29 async fn op29_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -478,6 +499,7 @@ pub trait Default: super::Error /// Op2Get - GET /op2 async fn op2_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -486,6 +508,7 @@ pub trait Default: super::Error /// Op30Get - GET /op30 async fn op30_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -494,6 +517,7 @@ pub trait Default: super::Error /// Op31Get - GET /op31 async fn op31_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -502,6 +526,7 @@ pub trait Default: super::Error /// Op32Get - GET /op32 async fn op32_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -510,6 +535,7 @@ pub trait Default: super::Error /// Op33Get - GET /op33 async fn op33_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -518,6 +544,7 @@ pub trait Default: super::Error /// Op34Get - GET /op34 async fn op34_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -526,6 +553,7 @@ pub trait Default: super::Error /// Op35Get - GET /op35 async fn op35_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -534,6 +562,7 @@ pub trait Default: super::Error /// Op36Get - GET /op36 async fn op36_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -542,6 +571,7 @@ pub trait Default: super::Error /// Op37Get - GET /op37 async fn op37_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -550,6 +580,7 @@ pub trait Default: super::Error /// Op3Get - GET /op3 async fn op3_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -558,6 +589,7 @@ pub trait Default: super::Error /// Op4Get - GET /op4 async fn op4_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -566,6 +598,7 @@ pub trait Default: super::Error /// Op5Get - GET /op5 async fn op5_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -574,6 +607,7 @@ pub trait Default: super::Error /// Op6Get - GET /op6 async fn op6_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -582,6 +616,7 @@ pub trait Default: super::Error /// Op7Get - GET /op7 async fn op7_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -590,6 +625,7 @@ pub trait Default: super::Error /// Op8Get - GET /op8 async fn op8_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -598,6 +634,7 @@ pub trait Default: super::Error /// Op9Get - GET /op9 async fn op9_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/src/header.rs +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 index 58fc026395c..7d30ad9f64c 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/src/models.rs +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/models.rs @@ -6,3 +6,57 @@ use validator::Validate; #[cfg(feature = "server")] use crate::header; use crate::{models, types::*}; + +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} 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 809270d4ddb..fb006693d67 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -2132,3 +2132,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index c84191061dd..fcab6ba59ec 100644 --- a/samples/server/petstore/rust-axum/output/ops-v3/src/types.rs +++ b/samples/server/petstore/rust-axum/output/ops-v3/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 8c6d24436b3..1563c088530 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 @@ -4,7 +4,7 @@ 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" +edition = "2024" publish = ["crates-io"] [features] @@ -19,6 +19,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 b2da825b2f9..5a8333854fb 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/another_fake.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/another_fake.rs index f3a796904b3..3909f91949f 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/another_fake.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/another_fake.rs @@ -26,6 +26,7 @@ pub trait AnotherFake: /// TestSpecialTags - PATCH /v2/another-fake/dummy async fn test_special_tags( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake.rs index 4a52de80e55..628096aba50 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake.rs @@ -124,6 +124,7 @@ pub trait Fake: super::ErrorHan /// Call123example - GET /v2/fake/operation-with-numeric-id async fn call123example( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -132,6 +133,7 @@ pub trait Fake: super::ErrorHan /// FakeOuterBooleanSerialize - POST /v2/fake/outer/boolean async fn fake_outer_boolean_serialize( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -141,6 +143,7 @@ pub trait Fake: super::ErrorHan /// FakeOuterCompositeSerialize - POST /v2/fake/outer/composite async fn fake_outer_composite_serialize( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -150,6 +153,7 @@ pub trait Fake: super::ErrorHan /// FakeOuterNumberSerialize - POST /v2/fake/outer/number async fn fake_outer_number_serialize( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -159,6 +163,7 @@ pub trait Fake: super::ErrorHan /// FakeOuterStringSerialize - POST /v2/fake/outer/string async fn fake_outer_string_serialize( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -168,6 +173,7 @@ pub trait Fake: super::ErrorHan /// FakeResponseWithNumericalDescription - GET /v2/fake/response-with-numerical-description async fn fake_response_with_numerical_description( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -176,6 +182,7 @@ pub trait Fake: super::ErrorHan /// HyphenParam - GET /v2/fake/hyphenParam/{hyphen-param} async fn hyphen_param( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -185,6 +192,7 @@ pub trait Fake: super::ErrorHan /// TestBodyWithQueryParams - PUT /v2/fake/body-with-query-params async fn test_body_with_query_params( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -197,6 +205,7 @@ pub trait Fake: super::ErrorHan /// TestClientModel - PATCH /v2/fake async fn test_client_model( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -208,6 +217,7 @@ pub trait Fake: super::ErrorHan /// TestEndpointParameters - POST /v2/fake async fn test_endpoint_parameters( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -220,6 +230,7 @@ pub trait Fake: super::ErrorHan /// TestEnumParameters - GET /v2/fake async fn test_enum_parameters( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -233,6 +244,7 @@ pub trait Fake: super::ErrorHan /// TestInlineAdditionalProperties - POST /v2/fake/inline-additionalProperties async fn test_inline_additional_properties( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -244,6 +256,7 @@ pub trait Fake: super::ErrorHan /// TestJsonFormData - GET /v2/fake/jsonFormData async fn test_json_form_data( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake_classname_tags123.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake_classname_tags123.rs index 4987f71681e..ee2b23e6f87 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake_classname_tags123.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/fake_classname_tags123.rs @@ -26,6 +26,7 @@ pub trait FakeClassnameTags123: /// TestClassname - PATCH /v2/fake_classname_test async fn test_classname( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/pet.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/pet.rs index fb739588ea4..713ccf267e0 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/pet.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/pet.rs @@ -94,6 +94,7 @@ pub trait Pet: super::ErrorHand /// AddPet - POST /v2/pet async fn add_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -105,6 +106,7 @@ pub trait Pet: super::ErrorHand /// DeletePet - DELETE /v2/pet/{petId} async fn delete_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -117,6 +119,7 @@ pub trait Pet: super::ErrorHand /// FindPetsByStatus - GET /v2/pet/findByStatus async fn find_pets_by_status( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -128,6 +131,7 @@ pub trait Pet: super::ErrorHand /// FindPetsByTags - GET /v2/pet/findByTags async fn find_pets_by_tags( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -139,6 +143,7 @@ pub trait Pet: super::ErrorHand /// GetPetById - GET /v2/pet/{petId} async fn get_pet_by_id( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -151,6 +156,7 @@ pub trait Pet: super::ErrorHand /// UpdatePet - PUT /v2/pet async fn update_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -162,6 +168,7 @@ pub trait Pet: super::ErrorHand /// UpdatePetWithForm - POST /v2/pet/{petId} async fn update_pet_with_form( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -174,6 +181,7 @@ pub trait Pet: super::ErrorHand /// UploadFile - POST /v2/pet/{petId}/uploadImage async fn upload_file( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/store.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/store.rs index c53e0608da9..4027411aabc 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/store.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/store.rs @@ -58,6 +58,7 @@ pub trait Store: super::ErrorHa /// DeleteOrder - DELETE /v2/store/order/{order_id} async fn delete_order( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -69,6 +70,7 @@ pub trait Store: super::ErrorHa /// GetInventory - GET /v2/store/inventory async fn get_inventory( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -80,6 +82,7 @@ pub trait Store: super::ErrorHa /// GetOrderById - GET /v2/store/order/{order_id} async fn get_order_by_id( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -91,6 +94,7 @@ pub trait Store: super::ErrorHa /// PlaceOrder - POST /v2/store/order async fn place_order( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/user.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/user.rs index 1c68461fdf6..6e0d6573743 100644 --- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/user.rs +++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/apis/user.rs @@ -94,6 +94,7 @@ pub trait User: super::ErrorHan /// CreateUser - POST /v2/user async fn create_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -105,6 +106,7 @@ pub trait User: super::ErrorHan /// CreateUsersWithArrayInput - POST /v2/user/createWithArray async fn create_users_with_array_input( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -116,6 +118,7 @@ pub trait User: super::ErrorHan /// CreateUsersWithListInput - POST /v2/user/createWithList async fn create_users_with_list_input( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -127,6 +130,7 @@ pub trait User: super::ErrorHan /// DeleteUser - DELETE /v2/user/{username} async fn delete_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -138,6 +142,7 @@ pub trait User: super::ErrorHan /// GetUserByName - GET /v2/user/{username} async fn get_user_by_name( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -149,6 +154,7 @@ pub trait User: super::ErrorHan /// LoginUser - GET /v2/user/login async fn login_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -160,6 +166,7 @@ pub trait User: super::ErrorHan /// LogoutUser - GET /v2/user/logout async fn logout_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -170,6 +177,7 @@ pub trait User: super::ErrorHan /// UpdateUser - PUT /v2/user/{username} async fn update_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- 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 @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 812eff3f5cb..69f5dda3e2e 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 @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct HyphenParamPathParams { @@ -155,10 +209,12 @@ pub struct UpdateUserPathParams { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct AdditionalPropertiesClass { #[serde(rename = "map_property")] + #[validate(custom(function = "check_xss_map_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub map_property: Option>, #[serde(rename = "map_of_map_property")] + #[validate(custom(function = "check_xss_map"))] #[serde(skip_serializing_if = "Option::is_none")] pub map_of_map_property: Option>>, @@ -223,7 +279,7 @@ impl std::str::FromStr for AdditionalPropertiesClass { None => { return std::result::Result::Err( "Missing value while parsing AdditionalPropertiesClass".to_string(), - ) + ); } }; @@ -261,8 +317,7 @@ impl std::convert::TryFrom> f 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 + r#"Invalid header value for AdditionalPropertiesClass - value: {hdr_value} is invalid {e}"# )), } } @@ -280,14 +335,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into AdditionalPropertiesClass - {}", - value, err + r#"Unable to convert header value '{value}' into AdditionalPropertiesClass - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -297,9 +350,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -362,7 +417,7 @@ impl std::str::FromStr for Animal { None => { return std::result::Result::Err( "Missing value while parsing Animal".to_string(), - ) + ); } }; @@ -380,7 +435,7 @@ impl std::str::FromStr for Animal { _ => { return std::result::Result::Err( "Unexpected key while parsing Animal".to_string(), - ) + ); } } } @@ -414,8 +469,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Animal - value: {hdr_value} is invalid {e}"# )), } } @@ -433,14 +487,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Animal - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -558,8 +610,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for AnimalFarm - value: {hdr_value} is invalid {e}"# )), } } @@ -577,14 +628,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into AnimalFarm - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -598,10 +647,12 @@ pub struct ApiResponse { pub code: Option, #[serde(rename = "type")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub r#type: Option, #[serde(rename = "message")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub message: Option, } @@ -670,7 +721,7 @@ impl std::str::FromStr for ApiResponse { None => { return std::result::Result::Err( "Missing value while parsing ApiResponse".to_string(), - ) + ); } }; @@ -692,7 +743,7 @@ impl std::str::FromStr for ApiResponse { _ => { return std::result::Result::Err( "Unexpected key while parsing ApiResponse".to_string(), - ) + ); } } } @@ -723,8 +774,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for ApiResponse - value: {hdr_value} is invalid {e}"# )), } } @@ -742,14 +792,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into ApiResponse - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -816,7 +864,7 @@ impl std::str::FromStr for ArrayOfArrayOfNumberOnly { None => { return std::result::Result::Err( "Missing value while parsing ArrayOfArrayOfNumberOnly".to_string(), - ) + ); } }; @@ -852,8 +900,7 @@ impl std::convert::TryFrom> fo 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 + r#"Invalid header value for ArrayOfArrayOfNumberOnly - value: {hdr_value} is invalid {e}"# )), } } @@ -871,14 +918,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ArrayOfArrayOfNumberOnly - {}", - value, err + r#"Unable to convert header value '{value}' into ArrayOfArrayOfNumberOnly - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -950,7 +995,7 @@ impl std::str::FromStr for ArrayOfNumberOnly { None => { return std::result::Result::Err( "Missing value while parsing ArrayOfNumberOnly".to_string(), - ) + ); } }; @@ -964,7 +1009,7 @@ impl std::str::FromStr for ArrayOfNumberOnly { _ => { return std::result::Result::Err( "Unexpected key while parsing ArrayOfNumberOnly".to_string(), - ) + ); } } } @@ -993,8 +1038,7 @@ impl std::convert::TryFrom> for Heade 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 + r#"Invalid header value for ArrayOfNumberOnly - value: {hdr_value} is invalid {e}"# )), } } @@ -1012,14 +1056,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ArrayOfNumberOnly - {}", - value, err + r#"Unable to convert header value '{value}' into ArrayOfNumberOnly - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1029,6 +1071,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue>, @@ -1042,6 +1085,7 @@ pub struct ArrayTest { /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "array_of_enum")] + #[validate(custom(function = "check_xss_vec_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub array_of_enum: Option>, } @@ -1128,7 +1172,7 @@ impl std::str::FromStr for ArrayTest { None => { return std::result::Result::Err( "Missing value while parsing ArrayTest".to_string(), - ) + ); } }; @@ -1139,30 +1183,30 @@ impl std::str::FromStr for ArrayTest { 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(), - ) + ); } } } @@ -1194,8 +1238,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for ArrayTest - value: {hdr_value} is invalid {e}"# )), } } @@ -1213,14 +1256,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into ArrayTest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1230,27 +1271,33 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Capitalization { #[serde(rename = "smallCamel")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub small_camel: Option, #[serde(rename = "CapitalCamel")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub capital_camel: Option, #[serde(rename = "small_Snake")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub small_snake: Option, #[serde(rename = "Capital_Snake")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub capital_snake: Option, #[serde(rename = "SCA_ETH_Flow_Points")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub sca_eth_flow_points: Option, /// Name of the pet #[serde(rename = "ATT_NAME")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub att_name: Option, } @@ -1340,7 +1387,7 @@ impl std::str::FromStr for Capitalization { None => { return std::result::Result::Err( "Missing value while parsing Capitalization".to_string(), - ) + ); } }; @@ -1374,7 +1421,7 @@ impl std::str::FromStr for Capitalization { _ => { return std::result::Result::Err( "Unexpected key while parsing Capitalization".to_string(), - ) + ); } } } @@ -1408,8 +1455,7 @@ impl std::convert::TryFrom> for HeaderVa 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 + r#"Invalid header value for Capitalization - value: {hdr_value} is invalid {e}"# )), } } @@ -1427,14 +1473,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into Capitalization - {}", - value, err + r#"Unable to convert header value '{value}' into Capitalization - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1444,9 +1488,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue, @@ -1516,7 +1562,7 @@ impl std::str::FromStr for Cat { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Cat".to_string()) + return std::result::Result::Err("Missing value while parsing Cat".to_string()); } }; @@ -1538,7 +1584,7 @@ impl std::str::FromStr for Cat { _ => { return std::result::Result::Err( "Unexpected key while parsing Cat".to_string(), - ) + ); } } } @@ -1571,8 +1617,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Cat - value: {hdr_value} is invalid {e}"# )), } } @@ -1589,13 +1634,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Cat - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1609,6 +1652,7 @@ pub struct Category { pub id: Option, #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } @@ -1672,7 +1716,7 @@ impl std::str::FromStr for Category { None => { return std::result::Result::Err( "Missing value while parsing Category".to_string(), - ) + ); } }; @@ -1690,7 +1734,7 @@ impl std::str::FromStr for Category { _ => { return std::result::Result::Err( "Unexpected key while parsing Category".to_string(), - ) + ); } } } @@ -1720,8 +1764,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Category - value: {hdr_value} is invalid {e}"# )), } } @@ -1739,14 +1782,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Category - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1757,6 +1798,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct ClassModel { #[serde(rename = "_class")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub _class: Option, } @@ -1773,10 +1815,11 @@ impl ClassModel { /// Should be implemented in a serde serializer impl std::fmt::Display for ClassModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - ._class - .as_ref() - .map(|_class| ["_class".to_string(), _class.to_string()].join(","))]; + let params: Vec> = vec![ + self._class + .as_ref() + .map(|_class| ["_class".to_string(), _class.to_string()].join(",")), + ]; write!( f, @@ -1812,7 +1855,7 @@ impl std::str::FromStr for ClassModel { None => { return std::result::Result::Err( "Missing value while parsing ClassModel".to_string(), - ) + ); } }; @@ -1826,7 +1869,7 @@ impl std::str::FromStr for ClassModel { _ => { return std::result::Result::Err( "Unexpected key while parsing ClassModel".to_string(), - ) + ); } } } @@ -1855,8 +1898,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for ClassModel - value: {hdr_value} is invalid {e}"# )), } } @@ -1874,14 +1916,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into ClassModel - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1891,6 +1931,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Client { #[serde(rename = "client")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub client: Option, } @@ -1907,10 +1948,11 @@ impl Client { /// Should be implemented in a serde serializer impl std::fmt::Display for Client { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .client - .as_ref() - .map(|client| ["client".to_string(), client.to_string()].join(","))]; + let params: Vec> = vec![ + self.client + .as_ref() + .map(|client| ["client".to_string(), client.to_string()].join(",")), + ]; write!( f, @@ -1946,7 +1988,7 @@ impl std::str::FromStr for Client { None => { return std::result::Result::Err( "Missing value while parsing Client".to_string(), - ) + ); } }; @@ -1960,7 +2002,7 @@ impl std::str::FromStr for Client { _ => { return std::result::Result::Err( "Unexpected key while parsing Client".to_string(), - ) + ); } } } @@ -1989,8 +2031,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Client - value: {hdr_value} is invalid {e}"# )), } } @@ -2008,14 +2049,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Client - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2025,13 +2064,16 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Dog { #[serde(rename = "className")] + #[validate(custom(function = "check_xss_string"))] pub class_name: String, #[serde(rename = "color")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, #[serde(rename = "breed")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub breed: Option, } @@ -2097,7 +2139,7 @@ impl std::str::FromStr for Dog { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Dog".to_string()) + return std::result::Result::Err("Missing value while parsing Dog".to_string()); } }; @@ -2119,7 +2161,7 @@ impl std::str::FromStr for Dog { _ => { return std::result::Result::Err( "Unexpected key while parsing Dog".to_string(), - ) + ); } } } @@ -2152,8 +2194,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Dog - value: {hdr_value} is invalid {e}"# )), } } @@ -2170,13 +2211,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Dog - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2184,35 +2223,39 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] -pub struct DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { +pub struct DollarSpecialLeftSquareBracketModelNameRightSquareBracket { #[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, + pub dollar_special_left_square_bracket_property_name_right_square_bracket: Option, } -impl DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { +impl DollarSpecialLeftSquareBracketModelNameRightSquareBracket { #[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, + pub fn new() -> DollarSpecialLeftSquareBracketModelNameRightSquareBracket { + DollarSpecialLeftSquareBracketModelNameRightSquareBracket { + dollar_special_left_square_bracket_property_name_right_square_bracket: None, } } } -/// Converts the DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket value to the Query Parameters representation (style=form, explode=false) +/// Converts the DollarSpecialLeftSquareBracketModelNameRightSquareBracket 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::fmt::Display for DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { +impl std::fmt::Display for DollarSpecialLeftSquareBracketModelNameRightSquareBracket { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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(",") - }), - + self.dollar_special_left_square_bracket_property_name_right_square_bracket + .as_ref() + .map( + |dollar_special_left_square_bracket_property_name_right_square_bracket| { + [ + "$special[property.name]".to_string(), + dollar_special_left_square_bracket_property_name_right_square_bracket + .to_string(), + ] + .join(",") + }, + ), ]; write!( @@ -2223,10 +2266,10 @@ impl std::fmt::Display for DollarSpecialLeftSquareBracketModelPeriodNameRightSqu } } -/// Converts Query Parameters representation (style=form, explode=false) to a DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket value +/// Converts Query Parameters representation (style=form, explode=false) to a DollarSpecialLeftSquareBracketModelNameRightSquareBracket value /// as specified in https://swagger.io/docs/specification/serialization/ /// Should be implemented in a serde deserializer -impl std::str::FromStr for DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket { +impl std::str::FromStr for DollarSpecialLeftSquareBracketModelNameRightSquareBracket { type Err = String; fn from_str(s: &str) -> std::result::Result { @@ -2234,8 +2277,7 @@ impl std::str::FromStr for DollarSpecialLeftSquareBracketModelPeriodNameRightSqu #[derive(Default)] #[allow(dead_code)] struct IntermediateRep { - pub dollar_special_left_square_bracket_property_period_name_right_square_bracket: - Vec, + pub dollar_special_left_square_bracket_property_name_right_square_bracket: Vec, } let mut intermediate_rep = IntermediateRep::default(); @@ -2247,15 +2289,15 @@ impl std::str::FromStr for DollarSpecialLeftSquareBracketModelPeriodNameRightSqu 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()) + None => return std::result::Result::Err("Missing value while parsing DollarSpecialLeftSquareBracketModelNameRightSquareBracket".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()) + "$special[property.name]" => intermediate_rep.dollar_special_left_square_bracket_property_name_right_square_bracket.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing DollarSpecialLeftSquareBracketModelNameRightSquareBracket".to_string()) } } @@ -2264,56 +2306,55 @@ impl std::str::FromStr for DollarSpecialLeftSquareBracketModelPeriodNameRightSqu } // 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(), + std::result::Result::Ok(DollarSpecialLeftSquareBracketModelNameRightSquareBracket { + dollar_special_left_square_bracket_property_name_right_square_bracket: intermediate_rep + .dollar_special_left_square_bracket_property_name_right_square_bracket + .into_iter() + .next(), }) } } -// Methods for converting between header::IntoHeaderValue and HeaderValue +// Methods for converting between header::IntoHeaderValue and HeaderValue #[cfg(feature = "server")] impl std::convert::TryFrom< - header::IntoHeaderValue, + header::IntoHeaderValue, > for HeaderValue { type Error = String; fn try_from( hdr_value: header::IntoHeaderValue< - DollarSpecialLeftSquareBracketModelPeriodNameRightSquareBracket, + DollarSpecialLeftSquareBracketModelNameRightSquareBracket, >, ) -> 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)) + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Invalid header value for DollarSpecialLeftSquareBracketModelNameRightSquareBracket - value: {hdr_value} is invalid {e}"# + )), } } } #[cfg(feature = "server")] impl std::convert::TryFrom - for header::IntoHeaderValue + 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) { + 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(err) => std::result::Result::Err(format!(r#"Unable to convert header value '{value}' into DollarSpecialLeftSquareBracketModelNameRightSquareBracket - {err}"#)) } }, - std::result::Result::Err(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(e) => std::result::Result::Err(format!(r#"Unable to convert header: {hdr_value:?} to string: {e}"#)) } } } @@ -2323,11 +2364,13 @@ impl std::convert::TryFrom pub struct EnumArrays { /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "just_symbol")] + #[validate(custom(function = "check_xss_string"))] #[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")] + #[validate(custom(function = "check_xss_vec_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub array_enum: Option>, @@ -2407,7 +2450,7 @@ impl std::str::FromStr for EnumArrays { None => { return std::result::Result::Err( "Missing value while parsing EnumArrays".to_string(), - ) + ); } }; @@ -2422,18 +2465,18 @@ impl std::str::FromStr for EnumArrays { 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(), - ) + ); } } } @@ -2464,8 +2507,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for EnumArrays - value: {hdr_value} is invalid {e}"# )), } } @@ -2483,14 +2525,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into EnumArrays - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2514,6 +2554,12 @@ pub enum EnumClass { LeftParenthesisXyzRightParenthesis, } +impl validator::Validate for EnumClass { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + impl std::fmt::Display for EnumClass { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { @@ -2532,7 +2578,7 @@ impl std::str::FromStr for EnumClass { "_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)), + _ => std::result::Result::Err(format!(r#"Value not valid: {s}"#)), } } } @@ -2542,11 +2588,13 @@ impl std::str::FromStr for EnumClass { pub struct EnumTest { /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "enum_string")] + #[validate(custom(function = "check_xss_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")] + #[validate(custom(function = "check_xss_string"))] pub enum_string_required: String, /// Note: inline enums are not fully supported by openapi-generator @@ -2560,6 +2608,7 @@ pub struct EnumTest { pub enum_number: Option, #[serde(rename = "outerEnum")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub outer_enum: Option, } @@ -2635,7 +2684,7 @@ impl std::str::FromStr for EnumTest { None => { return std::result::Result::Err( "Missing value while parsing EnumTest".to_string(), - ) + ); } }; @@ -2666,7 +2715,7 @@ impl std::str::FromStr for EnumTest { _ => { return std::result::Result::Err( "Unexpected key while parsing EnumTest".to_string(), - ) + ); } } } @@ -2703,8 +2752,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for EnumTest - value: {hdr_value} is invalid {e}"# )), } } @@ -2722,14 +2770,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into EnumTest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -2769,7 +2815,8 @@ pub struct FormatTest { #[serde(rename = "string")] #[validate( regex(path = *RE_FORMATTEST_STRING), - )] + custom(function = "check_xss_string"), + )] #[serde(skip_serializing_if = "Option::is_none")] pub string: Option, @@ -2793,7 +2840,7 @@ pub struct FormatTest { pub uuid: Option, #[serde(rename = "password")] - #[validate(length(min = 10, max = 64))] + #[validate(length(min = 10, max = 64), custom(function = "check_xss_string"))] pub password: String, } @@ -2925,7 +2972,7 @@ impl std::str::FromStr for FormatTest { None => { return std::result::Result::Err( "Missing value while parsing FormatTest".to_string(), - ) + ); } }; @@ -2964,13 +3011,13 @@ impl std::str::FromStr for FormatTest { 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( @@ -2994,7 +3041,7 @@ impl std::str::FromStr for FormatTest { _ => { return std::result::Result::Err( "Unexpected key while parsing FormatTest".to_string(), - ) + ); } } } @@ -3051,8 +3098,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for FormatTest - value: {hdr_value} is invalid {e}"# )), } } @@ -3070,14 +3116,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into FormatTest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3087,10 +3131,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct HasOnlyReadOnly { #[serde(rename = "bar")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub bar: Option, #[serde(rename = "foo")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub foo: Option, } @@ -3154,7 +3200,7 @@ impl std::str::FromStr for HasOnlyReadOnly { None => { return std::result::Result::Err( "Missing value while parsing HasOnlyReadOnly".to_string(), - ) + ); } }; @@ -3172,7 +3218,7 @@ impl std::str::FromStr for HasOnlyReadOnly { _ => { return std::result::Result::Err( "Unexpected key while parsing HasOnlyReadOnly".to_string(), - ) + ); } } } @@ -3202,8 +3248,7 @@ impl std::convert::TryFrom> for HeaderV 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 + r#"Invalid header value for HasOnlyReadOnly - value: {hdr_value} is invalid {e}"# )), } } @@ -3221,14 +3266,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into HasOnlyReadOnly - {}", - value, err + r#"Unable to convert header value '{value}' into HasOnlyReadOnly - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3238,6 +3281,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -3256,10 +3300,10 @@ impl List { /// Should be implemented in a serde serializer impl std::fmt::Display for List { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .param_123_list - .as_ref() - .map(|param_123_list| ["123-list".to_string(), param_123_list.to_string()].join(","))]; + let params: Vec> = + vec![self.param_123_list.as_ref().map(|param_123_list| { + ["123-list".to_string(), param_123_list.to_string()].join(",") + })]; write!( f, @@ -3293,7 +3337,7 @@ impl std::str::FromStr for List { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing List".to_string()) + return std::result::Result::Err("Missing value while parsing List".to_string()); } }; @@ -3307,7 +3351,7 @@ impl std::str::FromStr for List { _ => { return std::result::Result::Err( "Unexpected key while parsing List".to_string(), - ) + ); } } } @@ -3336,8 +3380,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for List - value: {hdr_value} is invalid {e}"# )), } } @@ -3354,13 +3397,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into List - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3370,18 +3411,21 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct MapTest { #[serde(rename = "map_map_of_string")] + #[validate(custom(function = "check_xss_map"))] #[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")] + #[validate(custom(function = "check_xss_map"))] #[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")] + #[validate(custom(function = "check_xss_map_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub map_of_enum_string: Option>, } @@ -3451,7 +3495,7 @@ impl std::str::FromStr for MapTest { None => { return std::result::Result::Err( "Missing value while parsing MapTest".to_string(), - ) + ); } }; @@ -3462,24 +3506,24 @@ impl std::str::FromStr for MapTest { 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(), - ) + ); } } } @@ -3510,8 +3554,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for MapTest - value: {hdr_value} is invalid {e}"# )), } } @@ -3529,14 +3572,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into MapTest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3554,6 +3595,7 @@ pub struct MixedPropertiesAndAdditionalPropertiesClass { pub date_time: Option>, #[serde(rename = "map")] + #[validate(custom(function = "check_xss_map_nested"))] #[serde(skip_serializing_if = "Option::is_none")] pub map: Option>, } @@ -3662,10 +3704,10 @@ impl std::convert::TryFrom 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)) + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Invalid header value for MixedPropertiesAndAdditionalPropertiesClass - value: {hdr_value} is invalid {e}"# + )), } } } @@ -3678,17 +3720,21 @@ impl std::convert::TryFrom 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::Ok(value) => { + match ::from_str( + value, + ) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) } - }, - std::result::Result::Err(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(err) => std::result::Result::Err(format!( + r#"Unable to convert header value '{value}' into MixedPropertiesAndAdditionalPropertiesClass - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), } } } @@ -3702,6 +3748,7 @@ pub struct Model200Response { pub name: Option, #[serde(rename = "class")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub class: Option, } @@ -3765,7 +3812,7 @@ impl std::str::FromStr for Model200Response { None => { return std::result::Result::Err( "Missing value while parsing Model200Response".to_string(), - ) + ); } }; @@ -3783,7 +3830,7 @@ impl std::str::FromStr for Model200Response { _ => { return std::result::Result::Err( "Unexpected key while parsing Model200Response".to_string(), - ) + ); } } } @@ -3813,8 +3860,7 @@ impl std::convert::TryFrom> for Header 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 + r#"Invalid header value for Model200Response - value: {hdr_value} is invalid {e}"# )), } } @@ -3832,14 +3878,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into Model200Response - {}", - value, err + r#"Unable to convert header value '{value}' into Model200Response - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -3857,6 +3901,7 @@ pub struct Name { pub snake_case: Option, #[serde(rename = "property")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub property: Option, @@ -3931,7 +3976,7 @@ impl std::str::FromStr for Name { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Name".to_string()) + return std::result::Result::Err("Missing value while parsing Name".to_string()); } }; @@ -3957,7 +4002,7 @@ impl std::str::FromStr for Name { _ => { return std::result::Result::Err( "Unexpected key while parsing Name".to_string(), - ) + ); } } } @@ -3993,8 +4038,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Name - value: {hdr_value} is invalid {e}"# )), } } @@ -4011,13 +4055,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Name - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -4043,10 +4085,11 @@ impl NumberOnly { /// Should be implemented in a serde serializer impl std::fmt::Display for NumberOnly { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .just_number - .as_ref() - .map(|just_number| ["JustNumber".to_string(), just_number.to_string()].join(","))]; + let params: Vec> = vec![ + self.just_number + .as_ref() + .map(|just_number| ["JustNumber".to_string(), just_number.to_string()].join(",")), + ]; write!( f, @@ -4082,7 +4125,7 @@ impl std::str::FromStr for NumberOnly { None => { return std::result::Result::Err( "Missing value while parsing NumberOnly".to_string(), - ) + ); } }; @@ -4096,7 +4139,7 @@ impl std::str::FromStr for NumberOnly { _ => { return std::result::Result::Err( "Unexpected key while parsing NumberOnly".to_string(), - ) + ); } } } @@ -4125,8 +4168,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for NumberOnly - value: {hdr_value} is invalid {e}"# )), } } @@ -4144,14 +4186,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into NumberOnly - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -4161,6 +4201,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct ObjectContainingObjectWithOnlyAdditionalProperties { #[serde(rename = "inner")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub inner: Option, } @@ -4251,10 +4292,10 @@ impl ) -> 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)) + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Invalid header value for ObjectContainingObjectWithOnlyAdditionalProperties - value: {hdr_value} is invalid {e}"# + )), } } } @@ -4270,14 +4311,10 @@ impl std::convert::TryFrom 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(err) => std::result::Result::Err(format!(r#"Unable to convert header value '{value}' into ObjectContainingObjectWithOnlyAdditionalProperties - {err}"#)) } }, - std::result::Result::Err(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(e) => std::result::Result::Err(format!(r#"Unable to convert header: {hdr_value:?} to string: {e}"#)) } } } @@ -4366,6 +4403,7 @@ pub struct Order { /// Order Status /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "status")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, @@ -4451,7 +4489,7 @@ impl std::str::FromStr for Order { None => { return std::result::Result::Err( "Missing value while parsing Order".to_string(), - ) + ); } }; @@ -4486,7 +4524,7 @@ impl std::str::FromStr for Order { _ => { return std::result::Result::Err( "Unexpected key while parsing Order".to_string(), - ) + ); } } } @@ -4520,8 +4558,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Order - value: {hdr_value} is invalid {e}"# )), } } @@ -4538,13 +4575,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Order - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -4593,6 +4628,7 @@ pub struct OuterComposite { pub my_number: Option, #[serde(rename = "my_string")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub my_string: Option, @@ -4665,7 +4701,7 @@ impl std::str::FromStr for OuterComposite { None => { return std::result::Result::Err( "Missing value while parsing OuterComposite".to_string(), - ) + ); } }; @@ -4687,7 +4723,7 @@ impl std::str::FromStr for OuterComposite { _ => { return std::result::Result::Err( "Unexpected key while parsing OuterComposite".to_string(), - ) + ); } } } @@ -4718,8 +4754,7 @@ impl std::convert::TryFrom> for HeaderVa 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 + r#"Invalid header value for OuterComposite - value: {hdr_value} is invalid {e}"# )), } } @@ -4737,14 +4772,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into OuterComposite - {}", - value, err + r#"Unable to convert header value '{value}' into OuterComposite - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -4768,6 +4801,12 @@ pub enum OuterEnum { Delivered, } +impl validator::Validate for OuterEnum { + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + impl std::fmt::Display for OuterEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { @@ -4786,7 +4825,7 @@ impl std::str::FromStr for OuterEnum { "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)), + _ => std::result::Result::Err(format!(r#"Value not valid: {s}"#)), } } } @@ -4882,22 +4921,27 @@ pub struct Pet { pub id: Option, #[serde(rename = "category")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub category: Option, #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] pub name: String, #[serde(rename = "photoUrls")] + #[validate(custom(function = "check_xss_vec_string"))] pub photo_urls: Vec, #[serde(rename = "tags")] + #[validate(nested)] #[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")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -4979,7 +5023,7 @@ impl std::str::FromStr for Pet { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Pet".to_string()) + return std::result::Result::Err("Missing value while parsing Pet".to_string()); } }; @@ -5002,12 +5046,12 @@ impl std::str::FromStr for Pet { "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( @@ -5016,7 +5060,7 @@ impl std::str::FromStr for Pet { _ => { return std::result::Result::Err( "Unexpected key while parsing Pet".to_string(), - ) + ); } } } @@ -5056,8 +5100,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Pet - value: {hdr_value} is invalid {e}"# )), } } @@ -5074,13 +5117,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Pet - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -5090,10 +5131,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct ReadOnlyFirst { #[serde(rename = "bar")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub bar: Option, #[serde(rename = "baz")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub baz: Option, } @@ -5157,7 +5200,7 @@ impl std::str::FromStr for ReadOnlyFirst { None => { return std::result::Result::Err( "Missing value while parsing ReadOnlyFirst".to_string(), - ) + ); } }; @@ -5175,7 +5218,7 @@ impl std::str::FromStr for ReadOnlyFirst { _ => { return std::result::Result::Err( "Unexpected key while parsing ReadOnlyFirst".to_string(), - ) + ); } } } @@ -5205,8 +5248,7 @@ impl std::convert::TryFrom> for HeaderVal 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 + r#"Invalid header value for ReadOnlyFirst - value: {hdr_value} is invalid {e}"# )), } } @@ -5224,14 +5266,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into ReadOnlyFirst - {}", - value, err + r#"Unable to convert header value '{value}' into ReadOnlyFirst - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -5258,10 +5298,11 @@ impl Return { /// Should be implemented in a serde serializer impl std::fmt::Display for Return { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .r#return - .as_ref() - .map(|r#return| ["return".to_string(), r#return.to_string()].join(","))]; + let params: Vec> = vec![ + self.r#return + .as_ref() + .map(|r#return| ["return".to_string(), r#return.to_string()].join(",")), + ]; write!( f, @@ -5297,7 +5338,7 @@ impl std::str::FromStr for Return { None => { return std::result::Result::Err( "Missing value while parsing Return".to_string(), - ) + ); } }; @@ -5311,7 +5352,7 @@ impl std::str::FromStr for Return { _ => { return std::result::Result::Err( "Unexpected key while parsing Return".to_string(), - ) + ); } } } @@ -5340,8 +5381,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Return - value: {hdr_value} is invalid {e}"# )), } } @@ -5359,14 +5399,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Return - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -5380,6 +5418,7 @@ pub struct Tag { pub id: Option, #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } @@ -5441,7 +5480,7 @@ impl std::str::FromStr for Tag { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Tag".to_string()) + return std::result::Result::Err("Missing value while parsing Tag".to_string()); } }; @@ -5459,7 +5498,7 @@ impl std::str::FromStr for Tag { _ => { return std::result::Result::Err( "Unexpected key while parsing Tag".to_string(), - ) + ); } } } @@ -5487,8 +5526,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Tag - value: {hdr_value} is invalid {e}"# )), } } @@ -5505,13 +5543,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Tag - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -5557,7 +5593,8 @@ pub struct TestEndpointParametersRequest { #[serde(rename = "string")] #[validate( regex(path = *RE_TESTENDPOINTPARAMETERSREQUEST_STRING), - )] + custom(function = "check_xss_string"), + )] #[serde(skip_serializing_if = "Option::is_none")] pub string: Option, @@ -5565,7 +5602,8 @@ pub struct TestEndpointParametersRequest { #[serde(rename = "pattern_without_delimiter")] #[validate( regex(path = *RE_TESTENDPOINTPARAMETERSREQUEST_PATTERN_WITHOUT_DELIMITER), - )] + custom(function = "check_xss_string"), + )] pub pattern_without_delimiter: String, /// None @@ -5589,12 +5627,13 @@ pub struct TestEndpointParametersRequest { /// None #[serde(rename = "password")] - #[validate(length(min = 10, max = 64))] + #[validate(length(min = 10, max = 64), custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, /// None #[serde(rename = "callback")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub callback: Option, } @@ -5724,7 +5763,7 @@ impl std::str::FromStr for TestEndpointParametersRequest { None => { return std::result::Result::Err( "Missing value while parsing TestEndpointParametersRequest".to_string(), - ) + ); } }; @@ -5816,8 +5855,7 @@ impl std::convert::TryFrom std::result::Result::Ok(value), std::result::Result::Err(e) => std::result::Result::Err(format!( - "Invalid header value for TestEndpointParametersRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for TestEndpointParametersRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -5829,17 +5867,19 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 TestEndpointParametersRequest - {}", - value, err)) + 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(e) => std::result::Result::Err( - format!("Unable to convert header: {:?} to string: {}", - hdr_value, e)) + std::result::Result::Err(err) => std::result::Result::Err(format!( + r#"Unable to convert header value '{value}' into TestEndpointParametersRequest - {err}"# + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + r#"Unable to convert header: {hdr_value:?} to string: {e}"# + )), } } } @@ -5850,6 +5890,7 @@ pub struct TestEnumParametersRequest { /// Form parameter enum test (string) /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "enum_form_string")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub enum_form_string: Option, } @@ -5907,7 +5948,7 @@ impl std::str::FromStr for TestEnumParametersRequest { None => { return std::result::Result::Err( "Missing value while parsing TestEnumParametersRequest".to_string(), - ) + ); } }; @@ -5921,7 +5962,7 @@ impl std::str::FromStr for TestEnumParametersRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing TestEnumParametersRequest".to_string(), - ) + ); } } } @@ -5950,8 +5991,7 @@ impl std::convert::TryFrom> f 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 TestEnumParametersRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for TestEnumParametersRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -5969,14 +6009,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into TestEnumParametersRequest - {}", - value, err + r#"Unable to convert header value '{value}' into TestEnumParametersRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -5987,10 +6025,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { return std::result::Result::Err( "Missing value while parsing TestJsonFormDataRequest".to_string(), - ) + ); } }; @@ -6066,7 +6106,7 @@ impl std::str::FromStr for TestJsonFormDataRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing TestJsonFormDataRequest".to_string(), - ) + ); } } } @@ -6104,8 +6144,7 @@ impl std::convert::TryFrom> for 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 TestJsonFormDataRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for TestJsonFormDataRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -6123,14 +6162,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into TestJsonFormDataRequest - {}", - value, err + r#"Unable to convert header value '{value}' into TestJsonFormDataRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -6141,11 +6178,13 @@ impl std::convert::TryFrom for header::IntoHeaderValue, /// Updated status of the pet #[serde(rename = "status")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -6209,7 +6248,7 @@ impl std::str::FromStr for UpdatePetWithFormRequest { None => { return std::result::Result::Err( "Missing value while parsing UpdatePetWithFormRequest".to_string(), - ) + ); } }; @@ -6227,7 +6266,7 @@ impl std::str::FromStr for UpdatePetWithFormRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing UpdatePetWithFormRequest".to_string(), - ) + ); } } } @@ -6257,8 +6296,7 @@ impl std::convert::TryFrom> fo 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 UpdatePetWithFormRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for UpdatePetWithFormRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -6276,14 +6314,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into UpdatePetWithFormRequest - {}", - value, err + r#"Unable to convert header value '{value}' into UpdatePetWithFormRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -6294,6 +6330,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, @@ -6367,7 +6404,7 @@ impl std::str::FromStr for UploadFileRequest { None => { return std::result::Result::Err( "Missing value while parsing UploadFileRequest".to_string(), - ) + ); } }; @@ -6385,7 +6422,7 @@ impl std::str::FromStr for UploadFileRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing UploadFileRequest".to_string(), - ) + ); } } } @@ -6415,8 +6452,7 @@ impl std::convert::TryFrom> for Heade 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 UploadFileRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for UploadFileRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -6434,14 +6470,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into UploadFileRequest - {}", - value, err + r#"Unable to convert header value '{value}' into UploadFileRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -6455,26 +6489,32 @@ pub struct User { pub id: Option, #[serde(rename = "username")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub username: Option, #[serde(rename = "firstName")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub first_name: Option, #[serde(rename = "lastName")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub last_name: Option, #[serde(rename = "email")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(rename = "password")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, #[serde(rename = "phone")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub phone: Option, @@ -6571,7 +6611,7 @@ impl std::str::FromStr for User { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing User".to_string()) + return std::result::Result::Err("Missing value while parsing User".to_string()); } }; @@ -6613,7 +6653,7 @@ impl std::str::FromStr for User { _ => { return std::result::Result::Err( "Unexpected key while parsing User".to_string(), - ) + ); } } } @@ -6649,8 +6689,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for User - value: {hdr_value} is invalid {e}"# )), } } @@ -6667,13 +6706,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into User - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {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 index 78108242499..39308cf6915 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -986,10 +986,7 @@ where .await; let claims = None.or(claims_in_auth_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1095,8 +1092,7 @@ where return Response::builder() .status(StatusCode::BAD_REQUEST) .body(Body::from(format!( - "Invalid header enum_header_string_array - {}", - err + "Invalid header enum_header_string_array - {err}" ))) .map_err(|e| { error!(error = ?e); @@ -1115,8 +1111,7 @@ where return Response::builder() .status(StatusCode::BAD_REQUEST) .body(Body::from(format!( - "Invalid header enum_header_string - {}", - err + "Invalid header enum_header_string - {err}" ))) .map_err(|e| { error!(error = ?e); @@ -1533,7 +1528,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header api_key - {}", err))) + .body(Body::from(format!("Invalid header api_key - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -1783,10 +1778,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -2192,10 +2184,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -2855,7 +2844,7 @@ where 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 }); + .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 }); } }; @@ -2872,7 +2861,7 @@ where 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 }); + .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 }); } }; @@ -3055,3 +3044,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index 05607ee6c48..c5d649a8629 100644 --- 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 @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 ddfb5dd9e07..8ff56b6ab74 100644 --- a/samples/server/petstore/rust-axum/output/petstore/Cargo.toml +++ b/samples/server/petstore/rust-axum/output/petstore/Cargo.toml @@ -4,7 +4,7 @@ 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" +edition = "2024" [features] default = ["server"] @@ -18,6 +18,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } diff --git a/samples/server/petstore/rust-axum/output/petstore/README.md b/samples/server/petstore/rust-axum/output/petstore/README.md index 63c40969b2f..d471b2d9ac1 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/petstore/src/apis/pet.rs b/samples/server/petstore/rust-axum/output/petstore/src/apis/pet.rs index 04ba515ac35..e198cf1fffe 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/apis/pet.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/apis/pet.rs @@ -98,6 +98,7 @@ pub trait Pet: super::ErrorHand /// AddPet - POST /v2/pet async fn add_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -109,6 +110,7 @@ pub trait Pet: super::ErrorHand /// DeletePet - DELETE /v2/pet/{petId} async fn delete_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -121,6 +123,7 @@ pub trait Pet: super::ErrorHand /// FindPetsByStatus - GET /v2/pet/findByStatus async fn find_pets_by_status( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -132,6 +135,7 @@ pub trait Pet: super::ErrorHand /// FindPetsByTags - GET /v2/pet/findByTags async fn find_pets_by_tags( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -143,6 +147,7 @@ pub trait Pet: super::ErrorHand /// GetPetById - GET /v2/pet/{petId} async fn get_pet_by_id( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -155,6 +160,7 @@ pub trait Pet: super::ErrorHand /// UpdatePet - PUT /v2/pet async fn update_pet( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -166,6 +172,7 @@ pub trait Pet: super::ErrorHand /// UpdatePetWithForm - POST /v2/pet/{petId} async fn update_pet_with_form( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -178,6 +185,7 @@ pub trait Pet: super::ErrorHand /// UploadFile - POST /v2/pet/{petId}/uploadImage async fn upload_file( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore/src/apis/store.rs b/samples/server/petstore/rust-axum/output/petstore/src/apis/store.rs index d564986ceaf..a4b3b99459c 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/apis/store.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/apis/store.rs @@ -58,6 +58,7 @@ pub trait Store: super::ErrorHa /// DeleteOrder - DELETE /v2/store/order/{orderId} async fn delete_order( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -69,6 +70,7 @@ pub trait Store: super::ErrorHa /// GetInventory - GET /v2/store/inventory async fn get_inventory( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -80,6 +82,7 @@ pub trait Store: super::ErrorHa /// GetOrderById - GET /v2/store/order/{orderId} async fn get_order_by_id( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -91,6 +94,7 @@ pub trait Store: super::ErrorHa /// PlaceOrder - POST /v2/store/order async fn place_order( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore/src/apis/user.rs b/samples/server/petstore/rust-axum/output/petstore/src/apis/user.rs index 5bf72896888..fb120849ce8 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/apis/user.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/apis/user.rs @@ -97,6 +97,7 @@ pub trait User: super::ErrorHan /// CreateUser - POST /v2/user async fn create_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -109,6 +110,7 @@ pub trait User: super::ErrorHan /// CreateUsersWithArrayInput - POST /v2/user/createWithArray async fn create_users_with_array_input( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -121,6 +123,7 @@ pub trait User: super::ErrorHan /// CreateUsersWithListInput - POST /v2/user/createWithList async fn create_users_with_list_input( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -133,6 +136,7 @@ pub trait User: super::ErrorHan /// DeleteUser - DELETE /v2/user/{username} async fn delete_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -145,6 +149,7 @@ pub trait User: super::ErrorHan /// GetUserByName - GET /v2/user/{username} async fn get_user_by_name( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -156,6 +161,7 @@ pub trait User: super::ErrorHan /// LoginUser - GET /v2/user/login async fn login_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -167,6 +173,7 @@ pub trait User: super::ErrorHan /// LogoutUser - GET /v2/user/logout async fn logout_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -178,6 +185,7 @@ pub trait User: super::ErrorHan /// UpdateUser - PUT /v2/user/{username} async fn update_user( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/petstore/src/header.rs b/samples/server/petstore/rust-axum/output/petstore/src/header.rs index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/header.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } diff --git a/samples/server/petstore/rust-axum/output/petstore/src/models.rs b/samples/server/petstore/rust-axum/output/petstore/src/models.rs index 01eb66c5e6e..107ff2617b5 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/models.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/models.rs @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct DeletePetHeaderParams { @@ -94,7 +148,7 @@ pub struct LoginUserQueryParams { #[serde(rename = "username")] #[validate( regex(path = *RE_LOGINUSERQUERYPARAMS_USERNAME), - )] + )] pub username: String, /// The password for login in clear text #[serde(rename = "password")] @@ -121,10 +175,12 @@ pub struct ApiResponse { pub code: Option, #[serde(rename = "type")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub r#type: Option, #[serde(rename = "message")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub message: Option, } @@ -193,7 +249,7 @@ impl std::str::FromStr for ApiResponse { None => { return std::result::Result::Err( "Missing value while parsing ApiResponse".to_string(), - ) + ); } }; @@ -215,7 +271,7 @@ impl std::str::FromStr for ApiResponse { _ => { return std::result::Result::Err( "Unexpected key while parsing ApiResponse".to_string(), - ) + ); } } } @@ -246,8 +302,7 @@ impl std::convert::TryFrom> for HeaderValue 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 + r#"Invalid header value for ApiResponse - value: {hdr_value} is invalid {e}"# )), } } @@ -265,14 +320,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue 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 + r#"Unable to convert header value '{value}' into ApiResponse - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -289,7 +342,8 @@ pub struct Category { #[serde(rename = "name")] #[validate( regex(path = *RE_CATEGORY_NAME), - )] + custom(function = "check_xss_string"), + )] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } @@ -357,7 +411,7 @@ impl std::str::FromStr for Category { None => { return std::result::Result::Err( "Missing value while parsing Category".to_string(), - ) + ); } }; @@ -375,7 +429,7 @@ impl std::str::FromStr for Category { _ => { return std::result::Result::Err( "Unexpected key while parsing Category".to_string(), - ) + ); } } } @@ -405,8 +459,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Category - value: {hdr_value} is invalid {e}"# )), } } @@ -424,14 +477,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Category - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -460,6 +511,7 @@ pub struct Order { /// Order Status /// Note: inline enums are not fully supported by openapi-generator #[serde(rename = "status")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, @@ -545,7 +597,7 @@ impl std::str::FromStr for Order { None => { return std::result::Result::Err( "Missing value while parsing Order".to_string(), - ) + ); } }; @@ -580,7 +632,7 @@ impl std::str::FromStr for Order { _ => { return std::result::Result::Err( "Unexpected key while parsing Order".to_string(), - ) + ); } } } @@ -614,8 +666,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Order - value: {hdr_value} is invalid {e}"# )), } } @@ -632,13 +683,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Order - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -653,22 +702,27 @@ pub struct Pet { pub id: Option, #[serde(rename = "category")] + #[validate(nested)] #[serde(skip_serializing_if = "Option::is_none")] pub category: Option, #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] pub name: String, #[serde(rename = "photoUrls")] + #[validate(custom(function = "check_xss_vec_string"))] pub photo_urls: Vec, #[serde(rename = "tags")] + #[validate(nested)] #[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")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -750,7 +804,7 @@ impl std::str::FromStr for Pet { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Pet".to_string()) + return std::result::Result::Err("Missing value while parsing Pet".to_string()); } }; @@ -773,12 +827,12 @@ impl std::str::FromStr for Pet { "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( @@ -787,7 +841,7 @@ impl std::str::FromStr for Pet { _ => { return std::result::Result::Err( "Unexpected key while parsing Pet".to_string(), - ) + ); } } } @@ -827,8 +881,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Pet - value: {hdr_value} is invalid {e}"# )), } } @@ -845,13 +898,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Pet - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -866,6 +917,7 @@ pub struct Tag { pub id: Option, #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } @@ -927,7 +979,7 @@ impl std::str::FromStr for Tag { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing Tag".to_string()) + return std::result::Result::Err("Missing value while parsing Tag".to_string()); } }; @@ -945,7 +997,7 @@ impl std::str::FromStr for Tag { _ => { return std::result::Result::Err( "Unexpected key while parsing Tag".to_string(), - ) + ); } } } @@ -973,8 +1025,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for Tag - value: {hdr_value} is invalid {e}"# )), } } @@ -991,13 +1042,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into Tag - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1008,11 +1057,13 @@ impl std::convert::TryFrom for header::IntoHeaderValue { pub struct UpdatePetWithFormRequest { /// Updated name of the pet #[serde(rename = "name")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// Updated status of the pet #[serde(rename = "status")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -1076,7 +1127,7 @@ impl std::str::FromStr for UpdatePetWithFormRequest { None => { return std::result::Result::Err( "Missing value while parsing UpdatePetWithFormRequest".to_string(), - ) + ); } }; @@ -1094,7 +1145,7 @@ impl std::str::FromStr for UpdatePetWithFormRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing UpdatePetWithFormRequest".to_string(), - ) + ); } } } @@ -1124,8 +1175,7 @@ impl std::convert::TryFrom> fo 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 UpdatePetWithFormRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for UpdatePetWithFormRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -1143,14 +1193,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into UpdatePetWithFormRequest - {}", - value, err + r#"Unable to convert header value '{value}' into UpdatePetWithFormRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1161,6 +1209,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, @@ -1234,7 +1283,7 @@ impl std::str::FromStr for UploadFileRequest { None => { return std::result::Result::Err( "Missing value while parsing UploadFileRequest".to_string(), - ) + ); } }; @@ -1252,7 +1301,7 @@ impl std::str::FromStr for UploadFileRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing UploadFileRequest".to_string(), - ) + ); } } } @@ -1282,8 +1331,7 @@ impl std::convert::TryFrom> for Heade 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 UploadFileRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for UploadFileRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -1301,14 +1349,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into UploadFileRequest - {}", - value, err + r#"Unable to convert header value '{value}' into UploadFileRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -1323,26 +1369,32 @@ pub struct User { pub id: Option, #[serde(rename = "username")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub username: Option, #[serde(rename = "firstName")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub first_name: Option, #[serde(rename = "lastName")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub last_name: Option, #[serde(rename = "email")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub email: Option, #[serde(rename = "password")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, #[serde(rename = "phone")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub phone: Option, @@ -1439,7 +1491,7 @@ impl std::str::FromStr for User { let val = match string_iter.next() { Some(x) => x, None => { - return std::result::Result::Err("Missing value while parsing User".to_string()) + return std::result::Result::Err("Missing value while parsing User".to_string()); } }; @@ -1481,7 +1533,7 @@ impl std::str::FromStr for User { _ => { return std::result::Result::Err( "Unexpected key while parsing User".to_string(), - ) + ); } } } @@ -1517,8 +1569,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 + r#"Invalid header value for User - value: {hdr_value} is invalid {e}"# )), } } @@ -1535,13 +1586,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { 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 + r#"Unable to convert header value '{value}' into User - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {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 index 61b1f7eb1e3..2444933c3c7 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -197,7 +197,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header api_key - {}", err))) + .body(Body::from(format!("Invalid header api_key - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -447,10 +447,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -872,10 +869,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1149,10 +1143,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1235,10 +1226,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1322,10 +1310,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1401,10 +1386,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1592,7 +1574,7 @@ where 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 }); + .body(Body::from(format!("An internal server error occurred handling set_cookie header - {e}"))).map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }); } }; @@ -1607,7 +1589,7 @@ where 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 }); + .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 }); } }; @@ -1624,7 +1606,7 @@ where 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 }); + .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 }); } }; @@ -1695,10 +1677,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1784,10 +1763,7 @@ where .await; let claims = None.or(claims_in_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -1835,3 +1811,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/petstore/src/types.rs b/samples/server/petstore/rust-axum/output/petstore/src/types.rs index 484ff7cdeb6..149d3613ea9 100644 --- a/samples/server/petstore/rust-axum/output/petstore/src/types.rs +++ b/samples/server/petstore/rust-axum/output/petstore/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 cb11bcca61c..07e3e08d56f 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 @@ -3,7 +3,7 @@ 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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 caa358e1279..2dd7acca656 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/apis/default.rs b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/apis/default.rs index 1d5fe4c2387..3c050b37a41 100644 --- a/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/ping-bearer-auth/src/apis/default.rs @@ -24,6 +24,7 @@ pub trait Default: super::Error /// PingGet - GET /ping async fn ping_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- 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 @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 index 58fc026395c..7d30ad9f64c 100644 --- 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 @@ -6,3 +6,57 @@ use validator::Validate; #[cfg(feature = "server")] use crate::header; use crate::{models, types::*}; + +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} 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 1f3c2398441..164176b0f40 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -55,10 +55,7 @@ where .await; let claims = None.or(claims_in_auth_header); let Some(claims) = claims else { - return Response::builder() - .status(StatusCode::UNAUTHORIZED) - .body(Body::empty()) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); + return response_with_status_code_only(StatusCode::UNAUTHORIZED); }; #[allow(clippy::redundant_closure)] @@ -102,3 +99,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index 1386785787e..8b410453316 100644 --- 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 @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 index 4c631cf217a..fc74d6ceba8 100644 --- 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 @@ -1 +1 @@ -7.14.0-SNAPSHOT +7.15.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 index b4f12bee0a9..58582e6cc91 100644 --- 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 @@ -3,7 +3,7 @@ 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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 index e37fc17ea47..bc32cdffe9b 100644 --- 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 @@ -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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT 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 index 27663093e97..7d6df7c3c3a 100644 --- 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 @@ -22,6 +22,7 @@ pub trait Default: super::Error /// EndpointGet - GET /endpoint async fn endpoint_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- 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 @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 index 1901b7d8c75..70e9eed4f54 100644 --- 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 @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct EndpointGetQueryParams { 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 index 770a62b1d45..529c8e0a5fc 100644 --- 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -88,3 +88,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index 1ff96e3b4f6..57736940b89 100644 --- 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 @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 f5bb057bbf1..b4dc76fd956 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 @@ -3,7 +3,7 @@ name = "rust-axum-header-uui" version = "0.1.9" authors = ["OpenAPI Generator team and contributors"] description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" -edition = "2021" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 dc95c33407b..f077f3ef697 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/apis/default.rs b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/apis/default.rs index f6a1555ba92..dc6c4a3664e 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/apis/default.rs @@ -22,6 +22,7 @@ pub trait Default: super::Error /// UsersPost - POST /users async fn users_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/header.rs b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/header.rs index 656550c1324..0cc4440b78f 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/header.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } @@ -205,11 +196,10 @@ impl TryFrom for IntoHeaderValue { match hdr_value.to_str() { Ok(hdr_value) => match uuid::Uuid::from_str(hdr_value) { Ok(uuid) => Ok(IntoHeaderValue(uuid)), - Err(e) => Err(format!("Unable to parse: {} as uuid - {}", hdr_value, e)), + Err(e) => Err(format!(r#"Unable to parse: {hdr_value} as uuid - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -222,8 +212,7 @@ impl TryFrom> for HeaderValue { match HeaderValue::from_bytes(hdr_value.0.as_bytes()) { Ok(hdr_value) => Ok(hdr_value), Err(e) => Err(format!( - "Unable to convert {:?} to a header: {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/models.rs b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/models.rs index 22862b9b8b3..0375e2ac210 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/models.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/models.rs @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct UsersPostHeaderParams { 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 7d9a061f8e6..b154b7f57ab 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -57,7 +57,7 @@ where Err(err) => { return Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from(format!("Invalid header some_uid - {}", err))) + .body(Body::from(format!("Invalid header some_uid - {err}"))) .map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR @@ -140,3 +140,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/types.rs b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/types.rs index 9b9fe7ed249..7fb6c7c85af 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/types.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-header-uuid/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 3226402e595..d84d17f5294 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 @@ -3,7 +3,7 @@ name = "rust-axum-oneof" 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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 99cf9d4c034..19e680a90b8 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/apis/default.rs b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/apis/default.rs index bd39840e27c..7f9dff64a5b 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/apis/default.rs @@ -22,6 +22,7 @@ pub trait Default: super::Error /// Foo - POST / async fn foo( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/header.rs b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/header.rs index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/header.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/models.rs b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/models.rs index 52e1f5bdd0f..dbf993dae26 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/models.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/models.rs @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Goodbye { @@ -14,9 +68,11 @@ pub struct Goodbye { #[serde(default = "Goodbye::_name_for_op")] #[serde(serialize_with = "Goodbye::_serialize_op")] #[serde(rename = "op")] + #[validate(custom(function = "check_xss_string"))] pub op: String, #[serde(rename = "d")] + #[validate(nested)] pub d: models::GoodbyeD, } @@ -86,7 +142,7 @@ impl std::str::FromStr for Goodbye { None => { return std::result::Result::Err( "Missing value while parsing Goodbye".to_string(), - ) + ); } }; @@ -105,7 +161,7 @@ impl std::str::FromStr for Goodbye { _ => { return std::result::Result::Err( "Unexpected key while parsing Goodbye".to_string(), - ) + ); } } } @@ -143,8 +199,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 Goodbye - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for Goodbye - value: {hdr_value} is invalid {e}"# )), } } @@ -162,14 +217,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into Goodbye - {}", - value, err + r#"Unable to convert header value '{value}' into Goodbye - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -179,6 +232,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct GoodbyeD { #[serde(rename = "goodbye_message")] + #[validate(custom(function = "check_xss_string"))] pub goodbye_message: String, } @@ -233,7 +287,7 @@ impl std::str::FromStr for GoodbyeD { None => { return std::result::Result::Err( "Missing value while parsing GoodbyeD".to_string(), - ) + ); } }; @@ -247,7 +301,7 @@ impl std::str::FromStr for GoodbyeD { _ => { return std::result::Result::Err( "Unexpected key while parsing GoodbyeD".to_string(), - ) + ); } } } @@ -280,8 +334,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 GoodbyeD - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for GoodbyeD - value: {hdr_value} is invalid {e}"# )), } } @@ -299,14 +352,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into GoodbyeD - {}", - value, err + r#"Unable to convert header value '{value}' into GoodbyeD - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -316,6 +367,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Greeting { #[serde(rename = "d")] + #[validate(nested)] pub d: models::GreetingD, #[serde(default = "Greeting::_name_for_op")] @@ -393,7 +445,7 @@ impl std::str::FromStr for Greeting { None => { return std::result::Result::Err( "Missing value while parsing Greeting".to_string(), - ) + ); } }; @@ -412,7 +464,7 @@ impl std::str::FromStr for Greeting { _ => { return std::result::Result::Err( "Unexpected key while parsing Greeting".to_string(), - ) + ); } } } @@ -450,8 +502,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 Greeting - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for Greeting - value: {hdr_value} is invalid {e}"# )), } } @@ -469,14 +520,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into Greeting - {}", - value, err + r#"Unable to convert header value '{value}' into Greeting - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -486,6 +535,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct GreetingD { #[serde(rename = "greet_message")] + #[validate(custom(function = "check_xss_string"))] pub greet_message: String, } @@ -540,7 +590,7 @@ impl std::str::FromStr for GreetingD { None => { return std::result::Result::Err( "Missing value while parsing GreetingD".to_string(), - ) + ); } }; @@ -554,7 +604,7 @@ impl std::str::FromStr for GreetingD { _ => { return std::result::Result::Err( "Unexpected key while parsing GreetingD".to_string(), - ) + ); } } } @@ -587,8 +637,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 GreetingD - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for GreetingD - value: {hdr_value} is invalid {e}"# )), } } @@ -606,14 +655,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into GreetingD - {}", - value, err + r#"Unable to convert header value '{value}' into GreetingD - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -626,9 +673,11 @@ pub struct Hello { #[serde(default = "Hello::_name_for_op")] #[serde(serialize_with = "Hello::_serialize_op")] #[serde(rename = "op")] + #[validate(custom(function = "check_xss_string"))] pub op: String, #[serde(rename = "d")] + #[validate(nested)] pub d: models::HelloD, } @@ -701,7 +750,7 @@ impl std::str::FromStr for Hello { None => { return std::result::Result::Err( "Missing value while parsing Hello".to_string(), - ) + ); } }; @@ -720,7 +769,7 @@ impl std::str::FromStr for Hello { _ => { return std::result::Result::Err( "Unexpected key while parsing Hello".to_string(), - ) + ); } } } @@ -758,8 +807,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 Hello - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for Hello - value: {hdr_value} is invalid {e}"# )), } } @@ -776,13 +824,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into Hello - {}", - value, err + r#"Unable to convert header value '{value}' into Hello - {err}"# )), }, std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -792,6 +838,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct HelloD { #[serde(rename = "welcome_message")] + #[validate(custom(function = "check_xss_string"))] pub welcome_message: String, } @@ -846,7 +893,7 @@ impl std::str::FromStr for HelloD { None => { return std::result::Result::Err( "Missing value while parsing HelloD".to_string(), - ) + ); } }; @@ -860,7 +907,7 @@ impl std::str::FromStr for HelloD { _ => { return std::result::Result::Err( "Unexpected key while parsing HelloD".to_string(), - ) + ); } } } @@ -893,8 +940,7 @@ impl std::convert::TryFrom> for HeaderValue { 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 HelloD - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for HelloD - value: {hdr_value} is invalid {e}"# )), } } @@ -912,14 +958,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue { std::result::Result::Ok(header::IntoHeaderValue(value)) } std::result::Result::Err(err) => std::result::Result::Err(format!( - "Unable to convert header value '{}' into HelloD - {}", - value, err + r#"Unable to convert header value '{value}' into HelloD - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } 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 de5ef9d4968..2991586f4a6 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -112,3 +112,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/types.rs b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/types.rs index 5c5197e19c1..416088221d2 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/types.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-oneof/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 aeb1b385d92..b1ae48cb29a 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 @@ -3,7 +3,7 @@ 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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 07673233cfd..0aecc86d10a 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-test/src/apis/default.rs b/samples/server/petstore/rust-axum/output/rust-axum-test/src/apis/default.rs index a4f22925474..08660f91101 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-test/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-test/src/apis/default.rs @@ -86,6 +86,7 @@ pub trait Default: super::Error /// AllOfGet - GET /allOf async fn all_of_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -96,6 +97,7 @@ pub trait Default: super::Error /// DummyGet - GET /dummy async fn dummy_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -104,6 +106,7 @@ pub trait Default: super::Error /// DummyPut - PUT /dummy async fn dummy_put( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -115,6 +118,7 @@ pub trait Default: super::Error /// FileResponseGet - GET /file_response async fn file_response_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -123,6 +127,7 @@ pub trait Default: super::Error /// GetStructuredYaml - GET /get-structured-yaml async fn get_structured_yaml( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -133,6 +138,7 @@ pub trait Default: super::Error /// HtmlPost - POST /html async fn html_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -142,6 +148,7 @@ pub trait Default: super::Error /// PostYaml - POST /post-yaml async fn post_yaml( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -153,6 +160,7 @@ pub trait Default: super::Error /// RawJsonGet - GET /raw_json async fn raw_json_get( &self, + method: &Method, host: &Host, cookies: &CookieJar, @@ -163,6 +171,7 @@ pub trait Default: super::Error /// SoloObjectPost - POST /solo-object async fn solo_object_post( &self, + method: &Method, host: &Host, cookies: &CookieJar, 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 index 7c530892fbf..970a99da88e 100644 --- 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 @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } 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 index 26c25bb354f..1d09683c000 100644 --- 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 @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct FooANullableContainer { @@ -88,7 +142,7 @@ impl std::str::FromStr for FooANullableContainer { None => { return std::result::Result::Err( "Missing value while parsing FooANullableContainer".to_string(), - ) + ); } }; @@ -130,8 +184,7 @@ impl std::convert::TryFrom> for H 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 FooANullableContainer - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooANullableContainer - value: {hdr_value} is invalid {e}"# )), } } @@ -149,14 +202,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooANullableContainer - {}", - value, err + r#"Unable to convert header value '{value}' into FooANullableContainer - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -229,10 +280,12 @@ impl ::std::str::FromStr for FooAdditionalPropertiesObject { #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct FooAllOfObject { #[serde(rename = "sampleProperty")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub sample_property: Option, #[serde(rename = "sampleBaseProperty")] + #[validate(custom(function = "check_xss_string"))] #[serde(skip_serializing_if = "Option::is_none")] pub sample_base_property: Option, } @@ -302,7 +355,7 @@ impl std::str::FromStr for FooAllOfObject { None => { return std::result::Result::Err( "Missing value while parsing FooAllOfObject".to_string(), - ) + ); } }; @@ -320,7 +373,7 @@ impl std::str::FromStr for FooAllOfObject { _ => { return std::result::Result::Err( "Unexpected key while parsing FooAllOfObject".to_string(), - ) + ); } } } @@ -350,8 +403,7 @@ impl std::convert::TryFrom> for HeaderVa 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 FooAllOfObject - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooAllOfObject - value: {hdr_value} is invalid {e}"# )), } } @@ -369,14 +421,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooAllOfObject - {}", - value, err + r#"Unable to convert header value '{value}' into FooAllOfObject - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -386,6 +436,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -405,16 +456,17 @@ impl FooBaseAllOf { impl std::fmt::Display for FooBaseAllOf { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let params: Vec> = - vec![self - .sample_base_property - .as_ref() - .map(|sample_base_property| { - [ - "sampleBaseProperty".to_string(), - sample_base_property.to_string(), - ] - .join(",") - })]; + vec![ + self.sample_base_property + .as_ref() + .map(|sample_base_property| { + [ + "sampleBaseProperty".to_string(), + sample_base_property.to_string(), + ] + .join(",") + }), + ]; write!( f, @@ -450,7 +502,7 @@ impl std::str::FromStr for FooBaseAllOf { None => { return std::result::Result::Err( "Missing value while parsing FooBaseAllOf".to_string(), - ) + ); } }; @@ -464,7 +516,7 @@ impl std::str::FromStr for FooBaseAllOf { _ => { return std::result::Result::Err( "Unexpected key while parsing FooBaseAllOf".to_string(), - ) + ); } } } @@ -493,8 +545,7 @@ impl std::convert::TryFrom> for HeaderValu 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 FooBaseAllOf - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooBaseAllOf - value: {hdr_value} is invalid {e}"# )), } } @@ -512,14 +563,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooBaseAllOf - {}", - value, err + r#"Unable to convert header value '{value}' into FooBaseAllOf - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -529,9 +578,11 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -591,7 +642,7 @@ impl std::str::FromStr for FooDummyPutRequest { None => { return std::result::Result::Err( "Missing value while parsing FooDummyPutRequest".to_string(), - ) + ); } }; @@ -609,7 +660,7 @@ impl std::str::FromStr for FooDummyPutRequest { _ => { return std::result::Result::Err( "Unexpected key while parsing FooDummyPutRequest".to_string(), - ) + ); } } } @@ -643,8 +694,7 @@ impl std::convert::TryFrom> for Head 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 FooDummyPutRequest - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooDummyPutRequest - value: {hdr_value} is invalid {e}"# )), } } @@ -662,14 +712,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooDummyPutRequest - {}", - value, err + r#"Unable to convert header value '{value}' into FooDummyPutRequest - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -681,6 +729,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -697,10 +746,11 @@ impl FooGetYamlResponse { /// Should be implemented in a serde serializer impl std::fmt::Display for FooGetYamlResponse { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let params: Vec> = vec![self - .value - .as_ref() - .map(|value| ["value".to_string(), value.to_string()].join(","))]; + let params: Vec> = vec![ + self.value + .as_ref() + .map(|value| ["value".to_string(), value.to_string()].join(",")), + ]; write!( f, @@ -736,7 +786,7 @@ impl std::str::FromStr for FooGetYamlResponse { None => { return std::result::Result::Err( "Missing value while parsing FooGetYamlResponse".to_string(), - ) + ); } }; @@ -750,7 +800,7 @@ impl std::str::FromStr for FooGetYamlResponse { _ => { return std::result::Result::Err( "Unexpected key while parsing FooGetYamlResponse".to_string(), - ) + ); } } } @@ -779,8 +829,7 @@ impl std::convert::TryFrom> for Head 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 FooGetYamlResponse - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooGetYamlResponse - value: {hdr_value} is invalid {e}"# )), } } @@ -798,14 +847,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooGetYamlResponse - {}", - value, err + r#"Unable to convert header value '{value}' into FooGetYamlResponse - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -816,6 +863,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue, } @@ -871,7 +919,7 @@ impl std::str::FromStr for FooObjectOfObjects { None => { return std::result::Result::Err( "Missing value while parsing FooObjectOfObjects".to_string(), - ) + ); } }; @@ -886,7 +934,7 @@ impl std::str::FromStr for FooObjectOfObjects { _ => { return std::result::Result::Err( "Unexpected key while parsing FooObjectOfObjects".to_string(), - ) + ); } } } @@ -915,8 +963,7 @@ impl std::convert::TryFrom> for Head 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 FooObjectOfObjects - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooObjectOfObjects - value: {hdr_value} is invalid {e}"# )), } } @@ -934,14 +981,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooObjectOfObjects - {}", - value, err + r#"Unable to convert header value '{value}' into FooObjectOfObjects - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {e}"# )), } } @@ -951,6 +996,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue { return std::result::Result::Err( "Missing value while parsing FooObjectOfObjectsInner".to_string(), - ) + ); } }; @@ -1034,7 +1080,7 @@ impl std::str::FromStr for FooObjectOfObjectsInner { _ => { return std::result::Result::Err( "Unexpected key while parsing FooObjectOfObjectsInner".to_string(), - ) + ); } } } @@ -1068,8 +1114,7 @@ impl std::convert::TryFrom> for 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 FooObjectOfObjectsInner - value: {} is invalid {}", - hdr_value, e + r#"Invalid header value for FooObjectOfObjectsInner - value: {hdr_value} is invalid {e}"# )), } } @@ -1087,14 +1132,12 @@ impl std::convert::TryFrom for header::IntoHeaderValue std::result::Result::Err(format!( - "Unable to convert header value '{}' into FooObjectOfObjectsInner - {}", - value, err + r#"Unable to convert header value '{value}' into FooObjectOfObjectsInner - {err}"# )), } } std::result::Result::Err(e) => std::result::Result::Err(format!( - "Unable to convert header: {:?} to string: {}", - hdr_value, e + r#"Unable to convert header: {hdr_value:?} to string: {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 index e2f452b1e94..7c353c0633c 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -679,3 +679,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} 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 index 3f7fb60f4cf..999330a74f5 100644 --- 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 @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None, 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 4c631cf217a..fc74d6ceba8 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.14.0-SNAPSHOT +7.15.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 8c50febe8cc..98296234e9d 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 @@ -3,7 +3,7 @@ name = "rust-axum-validation-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" +edition = "2024" [features] default = ["server"] @@ -17,6 +17,7 @@ conversion = [ ] [dependencies] +ammonia = "4" async-trait = "0.1" axum = { version = "0.8", features = ["multipart"] } axum-extra = { version = "0.10", features = ["cookie", "query"] } 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 a57dff09f26..c281b779f0b 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.14.0-SNAPSHOT +- Generator version: 7.15.0-SNAPSHOT diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/apis/default.rs b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/apis/default.rs index 7f26aebebd6..35fd5a37d51 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/apis/default.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/apis/default.rs @@ -22,6 +22,7 @@ pub trait Default: super::Error /// MailPut - PUT /mail async fn mail_put( &self, + method: &Method, host: &Host, cookies: &CookieJar, diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/header.rs b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/header.rs index 7c530892fbf..970a99da88e 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/header.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/header.rs @@ -31,14 +31,12 @@ macro_rules! ihv_generate { 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 + r#"Unable to parse {} as a string: {e}"#, + stringify!($t) )), }, Err(e) => Err(format!( - "Unable to parse header {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header {hdr_value:?} as a string - {e}"# )), } } @@ -82,8 +80,7 @@ impl TryFrom for IntoHeaderValue> { .collect(), )), Err(e) => Err(format!( - "Unable to parse header: {:?} as a string - {}", - hdr_value, e + r#"Unable to parse header: {hdr_value:?} as a string - {e}"# )), } } @@ -96,8 +93,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} into a header - {e}"# )), } } @@ -111,7 +107,7 @@ impl TryFrom for IntoHeaderValue { 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)), + Err(e) => Err(format!(r#"Unable to convert header {hdr_value:?} to {e}"#)), } } } @@ -123,8 +119,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -139,11 +134,10 @@ impl TryFrom for IntoHeaderValue { 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!(r#"Unable to parse bool from {hdr_value} - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert {:?} from a header {}", - hdr_value, e + r#"Unable to convert {hdr_value:?} from a header {e}"# )), } } @@ -156,8 +150,7 @@ impl TryFrom> for HeaderValue { 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 + r#"Unable to convert: {hdr_value:?} into a header: {e}"# )), } } @@ -172,11 +165,10 @@ impl TryFrom for IntoHeaderValue> { 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!(r#"Unable to parse: {hdr_value} as date - {e}"#)), }, Err(e) => Err(format!( - "Unable to convert header {:?} to string {}", - hdr_value, e + r#"Unable to convert header {hdr_value:?} to string {e}"# )), } } @@ -189,8 +181,7 @@ impl TryFrom>> for HeaderValue { 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 + r#"Unable to convert {hdr_value:?} to a header: {e}"# )), } } diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/models.rs b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/models.rs index 5f38b9e1c5d..224c792d065 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/models.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/models.rs @@ -7,6 +7,60 @@ use validator::Validate; use crate::header; use crate::{models, types::*}; +#[allow(dead_code)] +pub fn check_xss_string(v: &str) -> std::result::Result<(), validator::ValidationError> { + if ammonia::is_html(v) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_vec_string(v: &[String]) -> std::result::Result<(), validator::ValidationError> { + if v.iter().any(|i| ammonia::is_html(i)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_string( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| ammonia::is_html(v)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map_nested( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> +where + T: validator::Validate, +{ + if v.keys().any(|k| ammonia::is_html(k)) || v.values().any(|v| v.validate().is_err()) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + +#[allow(dead_code)] +pub fn check_xss_map( + v: &std::collections::HashMap, +) -> std::result::Result<(), validator::ValidationError> { + if v.keys().any(|k| ammonia::is_html(k)) { + std::result::Result::Err(validator::ValidationError::new("xss detected")) + } else { + std::result::Result::Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct Email(String); 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 baf5fed751f..c132fc8c9cb 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 @@ -3,7 +3,7 @@ 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 http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode, header::CONTENT_TYPE}; use tracing::error; use validator::{Validate, ValidationErrors}; @@ -68,3 +68,12 @@ where StatusCode::INTERNAL_SERVER_ERROR }) } + +#[allow(dead_code)] +#[inline] +fn response_with_status_code_only(code: StatusCode) -> Result { + Response::builder() + .status(code) + .body(Body::empty()) + .map_err(|_| code) +} diff --git a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/types.rs b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/types.rs index 2bfd140e5cf..0a808e5a90c 100644 --- a/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/types.rs +++ b/samples/server/petstore/rust-axum/output/rust-axum-validation-test/src/types.rs @@ -1,6 +1,6 @@ use std::{mem, str::FromStr}; -use base64::{engine::general_purpose, Engine}; +use base64::{Engine, engine::general_purpose}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -661,7 +661,7 @@ impl validator::ValidateEmail for Nullable where T: validator::ValidateEmail, { - fn as_email_string(&self) -> Option> { + fn as_email_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_email_string(), Self::Null => None, @@ -673,7 +673,7 @@ impl validator::ValidateUrl for Nullable where T: validator::ValidateUrl, { - fn as_url_string(&self) -> Option> { + fn as_url_string(&'_ self) -> Option> { match self { Self::Present(x) => x.as_url_string(), Self::Null => None,