From caf22add95b88ad3d3492ac849b1fb87e680bd5a Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Wed, 23 Apr 2025 17:13:53 +0900 Subject: [PATCH] [rust] Fixed nullable byte arrays (#20720) * [rust] fixed nullable byte arrays * Updated tests and samples * updated type test * Added double option support * updated samples --- .../codegen/languages/RustClientCodegen.java | 9 +++++ .../src/main/resources/rust/model.mustache | 4 +- .../main/resources/rust/model_mod.mustache | 39 +++++++++++++++++++ .../src/test/resources/3_0/rust/petstore.yaml | 4 ++ .../rust/hyper/petstore/docs/TypeTesting.md | 1 + .../rust/hyper/petstore/src/models/mod.rs | 37 ++++++++++++++++++ .../hyper/petstore/src/models/type_testing.rs | 4 ++ .../rust/hyper0x/petstore/docs/TypeTesting.md | 1 + .../rust/hyper0x/petstore/src/models/mod.rs | 37 ++++++++++++++++++ .../petstore/src/models/type_testing.rs | 4 ++ .../petstore/docs/TypeTesting.md | 1 + .../reqwest-trait/petstore/src/models/mod.rs | 37 ++++++++++++++++++ .../petstore/src/models/type_testing.rs | 4 ++ .../docs/TypeTesting.md | 1 + .../src/models/mod.rs | 37 ++++++++++++++++++ .../src/models/type_testing.rs | 4 ++ .../docs/TypeTesting.md | 1 + .../src/models/mod.rs | 37 ++++++++++++++++++ .../src/models/type_testing.rs | 4 ++ .../petstore-async/docs/TypeTesting.md | 1 + .../reqwest/petstore-async/src/models/mod.rs | 37 ++++++++++++++++++ .../petstore-async/src/models/type_testing.rs | 4 ++ .../petstore-avoid-box/docs/TypeTesting.md | 1 + .../petstore-avoid-box/src/models/mod.rs | 37 ++++++++++++++++++ .../src/models/type_testing.rs | 4 ++ .../docs/TypeTesting.md | 1 + .../petstore-awsv4signature/src/models/mod.rs | 37 ++++++++++++++++++ .../src/models/type_testing.rs | 4 ++ .../docs/FooTypeTesting.md | 1 + .../src/models/foo_type_testing.rs | 4 ++ .../src/models/mod.rs | 37 ++++++++++++++++++ .../rust/reqwest/petstore/docs/TypeTesting.md | 1 + .../rust/reqwest/petstore/src/models/mod.rs | 37 ++++++++++++++++++ .../petstore/src/models/type_testing.rs | 4 ++ .../reqwest/petstore/tests/type_testing.rs | 1 + 35 files changed, 475 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java index c93bec52c2e..33ae00f447b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java @@ -605,9 +605,18 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon additionalProperties.put("serdeWith", true); } + // Add a field for checking if a field is with optional or required in templates. + // This is useful in Mustache templates as it's not possible to do OR logic between variables. + property.vendorExtensions.put("isMandatory", !property.isNullable && property.required); + // If a property is a base64-encoded byte array, use `serde_with` for deserialization. if (property.isByteArray) { additionalProperties.put("serdeWith", true); + // If a byte array is both nullable and not required we need to include our own + // custom double option as serde_as does not work with serde_with's double_option. + if (property.isNullable && !property.required) { + additionalProperties.put("serdeAsDoubleOption", true); + } } } diff --git a/modules/openapi-generator/src/main/resources/rust/model.mustache b/modules/openapi-generator/src/main/resources/rust/model.mustache index 98cc4f76f9f..a4970abf429 100644 --- a/modules/openapi-generator/src/main/resources/rust/model.mustache +++ b/modules/openapi-generator/src/main/resources/rust/model.mustache @@ -129,9 +129,9 @@ pub struct {{{classname}}} { /// {{{.}}} {{/description}} {{#isByteArray}} - {{#required}}#[serde_as(as = "serde_with::base64::Base64")]{{/required}}{{^required}}#[serde_as(as = "Option")]{{/required}} + {{#vendorExtensions.isMandatory}}#[serde_as(as = "serde_with::base64::Base64")]{{/vendorExtensions.isMandatory}}{{^vendorExtensions.isMandatory}}#[serde_as(as = "{{^serdeAsDoubleOption}}Option{{/serdeAsDoubleOption}}{{#serdeAsDoubleOption}}super::DoubleOption{{/serdeAsDoubleOption}}")]{{/vendorExtensions.isMandatory}} {{/isByteArray}} - #[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default, with = "::serde_with::rust::double_option"{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})] + #[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default{{^isByteArray}}, with = "::serde_with::rust::double_option"{{/isByteArray}}{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})] pub {{{name}}}: {{! ### Option Start }}{{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{! diff --git a/modules/openapi-generator/src/main/resources/rust/model_mod.mustache b/modules/openapi-generator/src/main/resources/rust/model_mod.mustache index 06bf92f019a..bdb080f6d1d 100644 --- a/modules/openapi-generator/src/main/resources/rust/model_mod.mustache +++ b/modules/openapi-generator/src/main/resources/rust/model_mod.mustache @@ -4,3 +4,42 @@ pub mod {{{classFilename}}}; pub use self::{{{classFilename}}}::{{{classname}}}; {{/model}} {{/models}} +{{#serdeAsDoubleOption}} +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} +{{/serdeAsDoubleOption}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml b/modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml index ced7450529e..983c6989dcf 100644 --- a/modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/rust/petstore.yaml @@ -935,6 +935,10 @@ components: bytes: type: string format: byte + nullableBytes: # Regression test for bug report in #20500 + type: string + format: byte + nullable: true decimal: type: string format: number diff --git a/samples/client/petstore/rust/hyper/petstore/docs/TypeTesting.md b/samples/client/petstore/rust/hyper/petstore/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/hyper/petstore/docs/TypeTesting.md +++ b/samples/client/petstore/rust/hyper/petstore/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/hyper/petstore/src/models/mod.rs b/samples/client/petstore/rust/hyper/petstore/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/hyper/petstore/src/models/mod.rs +++ b/samples/client/petstore/rust/hyper/petstore/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/hyper/petstore/src/models/type_testing.rs b/samples/client/petstore/rust/hyper/petstore/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/hyper/petstore/src/models/type_testing.rs +++ b/samples/client/petstore/rust/hyper/petstore/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/hyper0x/petstore/docs/TypeTesting.md b/samples/client/petstore/rust/hyper0x/petstore/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/hyper0x/petstore/docs/TypeTesting.md +++ b/samples/client/petstore/rust/hyper0x/petstore/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/hyper0x/petstore/src/models/mod.rs b/samples/client/petstore/rust/hyper0x/petstore/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/hyper0x/petstore/src/models/mod.rs +++ b/samples/client/petstore/rust/hyper0x/petstore/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/hyper0x/petstore/src/models/type_testing.rs b/samples/client/petstore/rust/hyper0x/petstore/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/hyper0x/petstore/src/models/type_testing.rs +++ b/samples/client/petstore/rust/hyper0x/petstore/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest-trait/petstore/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest-trait/petstore/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest-trait/petstore/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest-trait/petstore/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest-trait/petstore/src/models/mod.rs b/samples/client/petstore/rust/reqwest-trait/petstore/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest-trait/petstore/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest-trait/petstore/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest-trait/petstore/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest-trait/petstore/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest-trait/petstore/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest-trait/petstore/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-async-middleware/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-async-middleware/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-middleware/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-async-middleware/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-middleware/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async-tokensource/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-async/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-async/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-async/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-async/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-async/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore-async/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-async/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore-async/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-async/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-avoid-box/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-avoid-box/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore-avoid-box/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-avoid-box/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-avoid-box/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-awsv4signature/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/docs/FooTypeTesting.md b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/docs/FooTypeTesting.md index cc73c990687..76008e985b3 100644 --- a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/docs/FooTypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/docs/FooTypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/foo_type_testing.rs b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/foo_type_testing.rs index 4fe82f1015f..facf71f65c7 100644 --- a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/foo_type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/foo_type_testing.rs @@ -34,6 +34,9 @@ pub struct FooTypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl FooTypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/mod.rs index 6eb5170bcb7..fc3f2bacb46 100644 --- a/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore-model-name-prefix/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod foo_user; pub use self::foo_user::FooUser; pub mod foo_vehicle; pub use self::foo_vehicle::FooVehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore/docs/TypeTesting.md b/samples/client/petstore/rust/reqwest/petstore/docs/TypeTesting.md index 393d1b9bd27..27b8f262242 100644 --- a/samples/client/petstore/rust/reqwest/petstore/docs/TypeTesting.md +++ b/samples/client/petstore/rust/reqwest/petstore/docs/TypeTesting.md @@ -12,6 +12,7 @@ Name | Type | Description | Notes **boolean** | **bool** | | **uuid** | [**uuid::Uuid**](uuid::Uuid.md) | | **bytes** | **String** | | +**nullable_bytes** | Option<**String**> | | [optional] **decimal** | **String** | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/samples/client/petstore/rust/reqwest/petstore/src/models/mod.rs b/samples/client/petstore/rust/reqwest/petstore/src/models/mod.rs index ee02ccdd530..70b354278ab 100644 --- a/samples/client/petstore/rust/reqwest/petstore/src/models/mod.rs +++ b/samples/client/petstore/rust/reqwest/petstore/src/models/mod.rs @@ -42,3 +42,40 @@ pub mod user; pub use self::user::User; pub mod vehicle; pub use self::vehicle::Vehicle; +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} diff --git a/samples/client/petstore/rust/reqwest/petstore/src/models/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore/src/models/type_testing.rs index 9e797e82298..1c406069fec 100644 --- a/samples/client/petstore/rust/reqwest/petstore/src/models/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore/src/models/type_testing.rs @@ -34,6 +34,9 @@ pub struct TypeTesting { #[serde_as(as = "serde_with::base64::Base64")] #[serde(rename = "bytes")] pub bytes: Vec, + #[serde_as(as = "super::DoubleOption")] + #[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")] + pub nullable_bytes: Option>>, #[serde(rename = "decimal")] pub decimal: String, } @@ -50,6 +53,7 @@ impl TypeTesting { boolean, uuid, bytes, + nullable_bytes: None, decimal, } } diff --git a/samples/client/petstore/rust/reqwest/petstore/tests/type_testing.rs b/samples/client/petstore/rust/reqwest/petstore/tests/type_testing.rs index 0ee189f7f5f..39351798878 100644 --- a/samples/client/petstore/rust/reqwest/petstore/tests/type_testing.rs +++ b/samples/client/petstore/rust/reqwest/petstore/tests/type_testing.rs @@ -14,6 +14,7 @@ fn test_types() { boolean: true, uuid: Uuid::new_v4(), bytes: vec![1, 2, 3, 4], + nullable_bytes: Some(Some(vec![1, 2, 3, 4])), decimal: String::from("foo"), }; assert_eq!(type_of(tt.int32), "i32");