[rust] Fixed nullable byte arrays (#20720)

* [rust] fixed nullable byte arrays

* Updated tests and samples

* updated type test

* Added double option support

* updated samples
This commit is contained in:
Ross Sullivan 2025-04-23 17:13:53 +09:00 committed by GitHub
parent e767496357
commit caf22add95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 475 additions and 2 deletions

View File

@ -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);
}
}
}

View File

@ -129,9 +129,9 @@ pub struct {{{classname}}} {
/// {{{.}}}
{{/description}}
{{#isByteArray}}
{{#required}}#[serde_as(as = "serde_with::base64::Base64")]{{/required}}{{^required}}#[serde_as(as = "Option<serde_with::base64::Base64>")]{{/required}}
{{#vendorExtensions.isMandatory}}#[serde_as(as = "serde_with::base64::Base64")]{{/vendorExtensions.isMandatory}}{{^vendorExtensions.isMandatory}}#[serde_as(as = "{{^serdeAsDoubleOption}}Option{{/serdeAsDoubleOption}}{{#serdeAsDoubleOption}}super::DoubleOption{{/serdeAsDoubleOption}}<serde_with::base64::Base64>")]{{/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}}{{!

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}
{{/serdeAsDoubleOption}}

View File

@ -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

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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)

View File

@ -34,6 +34,9 @@ pub struct FooTypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl FooTypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -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)

View File

@ -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<T>(PhantomData<T>);
impl<T, TAs> SerializeAs<Option<Option<T>>> for DoubleOption<TAs>
where
TAs: SerializeAs<T>,
{
fn serialize_as<S>(values: &Option<Option<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match values {
None => serializer.serialize_unit(),
Some(None) => serializer.serialize_none(),
Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::<T, TAs>::new(v)),
}
}
}
impl<'de, T, TAs> DeserializeAs<'de, Option<Option<T>>> for DoubleOption<TAs>
where
TAs: DeserializeAs<'de, T>,
T: std::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Some(
DeserializeAsWrap::<Option<T>, Option<TAs>>::deserialize(deserializer)?
.into_inner(),
))
}
}

View File

@ -34,6 +34,9 @@ pub struct TypeTesting {
#[serde_as(as = "serde_with::base64::Base64")]
#[serde(rename = "bytes")]
pub bytes: Vec<u8>,
#[serde_as(as = "super::DoubleOption<serde_with::base64::Base64>")]
#[serde(rename = "nullableBytes", default, skip_serializing_if = "Option::is_none")]
pub nullable_bytes: Option<Option<Vec<u8>>>,
#[serde(rename = "decimal")]
pub decimal: String,
}
@ -50,6 +53,7 @@ impl TypeTesting {
boolean,
uuid,
bytes,
nullable_bytes: None,
decimal,
}
}

View File

@ -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");