From b1ce2d07d3951e5c2ff9ff618f6434b08cfd082b Mon Sep 17 00:00:00 2001 From: Romain Lebran <75622063+rlebran-netwo@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:40:08 +0200 Subject: [PATCH] Add `decimal_float` feature. (#750) This feature aim to provide utoipa user's the ability to change the default type for Decimal to Number without the need of overriding the type everywhere much like rust_decimal expose an serde-with-float feature. This feature is mutually exclusive with `decimal` feature. --- README.md | 3 ++ scripts/test.sh | 1 + utoipa-gen/Cargo.toml | 1 + utoipa-gen/src/lib.rs | 3 ++ utoipa-gen/src/schema_type.rs | 19 ++++++++++-- utoipa-gen/tests/schema_derive_test.rs | 41 ++++++++++++++++++++++++++ utoipa/Cargo.toml | 1 + utoipa/src/lib.rs | 3 ++ 8 files changed, 70 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8c07678..16fcc5d9 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ and the `ipa` is _api_ reversed. Aaand... `ipa` is also an awesome type of beer - **decimal** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** it is interpreted as `String`. If you wish to change the format you need to override the type. See the `value_type` in [component derive docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html). +- **decimal_float** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** + it is interpreted as `Number`. This feature is mutually exclusive with **decimal** and allow to change the default type used in your + documentation for `Decimal` much like `serde_with_float` feature exposed by rust_decimal. - **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with format `uuid` in OpenAPI spec. - **ulid** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with diff --git a/scripts/test.sh b/scripts/test.sh index 3798394b..f51048d8 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -11,6 +11,7 @@ if [[ "$crate" == "utoipa" ]]; then elif [[ "$crate" == "utoipa-gen" ]]; then cargo test -p utoipa-gen --features utoipa/actix_extras,chrono,decimal,utoipa/uuid,uuid,utoipa/ulid,ulid,utoipa/url,url,utoipa/time,time,utoipa/repr,utoipa/smallvec,smallvec,rc_schema,utoipa/rc_schema + cargo test -p utoipa-gen --test schema_derive_test --features decimal_float cargo test -p utoipa-gen --test path_derive_auto_into_responses --features auto_into_responses,utoipa/uuid,uuid cargo test -p utoipa-gen --test path_derive_actix --test path_parameter_derive_actix --features actix_extras,utoipa/uuid,uuid cargo test -p utoipa-gen --test path_derive_auto_into_responses_actix --features actix_extras,utoipa/auto_into_responses,utoipa/uuid,uuid diff --git a/utoipa-gen/Cargo.toml b/utoipa-gen/Cargo.toml index 319320a6..d6afa059 100644 --- a/utoipa-gen/Cargo.toml +++ b/utoipa-gen/Cargo.toml @@ -44,6 +44,7 @@ actix_extras = ["regex", "syn/extra-traits"] chrono = [] yaml = [] decimal = [] +decimal_float = [] rocket_extras = ["regex", "syn/extra-traits"] non_strict_integers = [] uuid = ["dep:uuid"] diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs index 177d7eba..80208369 100644 --- a/utoipa-gen/src/lib.rs +++ b/utoipa-gen/src/lib.rs @@ -7,6 +7,9 @@ #![warn(missing_docs)] #![warn(rustdoc::broken_intra_doc_links)] +#[cfg(all(feature = "decimal", feature = "decimal_float"))] +compile_error!("`decimal` and `decimal_float` are mutually exclusive feature flags"); + use std::{mem, ops::Deref}; use component::schema::Schema; diff --git a/utoipa-gen/src/schema_type.rs b/utoipa-gen/src/schema_type.rs index d8efd820..d70d319e 100644 --- a/utoipa-gen/src/schema_type.rs +++ b/utoipa-gen/src/schema_type.rs @@ -32,6 +32,7 @@ impl SchemaType<'_> { #[cfg(not(any( feature = "chrono", feature = "decimal", + feature = "decimal_float", feature = "rocket_extras", feature = "uuid", feature = "ulid", @@ -45,6 +46,7 @@ impl SchemaType<'_> { #[cfg(any( feature = "chrono", feature = "decimal", + feature = "decimal_float", feature = "rocket_extras", feature = "uuid", feature = "ulid", @@ -59,7 +61,7 @@ impl SchemaType<'_> { primitive = is_primitive_chrono(name); } - #[cfg(feature = "decimal")] + #[cfg(any(feature = "decimal", feature = "decimal_float"))] if !primitive { primitive = is_primitive_rust_decimal(name); } @@ -172,7 +174,7 @@ fn is_primitive_chrono(name: &str) -> bool { } #[inline] -#[cfg(feature = "decimal")] +#[cfg(any(feature = "decimal", feature = "decimal_float"))] fn is_primitive_rust_decimal(name: &str) -> bool { matches!(name, "Decimal") } @@ -206,6 +208,9 @@ impl ToTokens for SchemaType<'_> { #[cfg(feature = "decimal")] "Decimal" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), + #[cfg(feature = "decimal_float")] + "Decimal" => tokens.extend(quote! { utoipa::openapi::SchemaType::Number }), + #[cfg(feature = "rocket_extras")] "PathBuf" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), @@ -283,6 +288,7 @@ impl Type<'_> { #[cfg(not(any( feature = "chrono", + feature = "decimal_float", feature = "uuid", feature = "ulid", feature = "url", @@ -294,6 +300,7 @@ impl Type<'_> { #[cfg(any( feature = "chrono", + feature = "decimal_float", feature = "uuid", feature = "ulid", feature = "url", @@ -307,6 +314,11 @@ impl Type<'_> { known_format = matches!(name, "DateTime" | "Date" | "NaiveDate" | "NaiveDateTime"); } + #[cfg(feature = "decimal_float")] + if !known_format { + known_format = matches!(name, "Decimal"); + } + #[cfg(feature = "uuid")] if !known_format { known_format = matches!(name, "Uuid"); @@ -387,6 +399,9 @@ impl ToTokens for Type<'_> { #[cfg(any(feature = "chrono", feature = "time"))] "Date" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Date) }), + #[cfg(any(feature = "decimal_float"))] + "Decimal" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Double) }), + #[cfg(feature = "uuid")] "Uuid" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Uuid) }), diff --git a/utoipa-gen/tests/schema_derive_test.rs b/utoipa-gen/tests/schema_derive_test.rs index 4be0224f..04027877 100644 --- a/utoipa-gen/tests/schema_derive_test.rs +++ b/utoipa-gen/tests/schema_derive_test.rs @@ -3131,6 +3131,47 @@ fn derive_struct_with_rust_decimal_with_type_override() { } } +#[cfg(feature = "decimal_float")] +#[test] +fn derive_struct_with_rust_decimal_float() { + use rust_decimal::Decimal; + + let post = api_doc! { + struct Post { + id: i32, + rating: Decimal, + } + }; + + assert_value! {post=> + "properties.id.type" = r#""integer""#, "Post id type" + "properties.id.format" = r#""int32""#, "Post id format" + "properties.rating.type" = r#""number""#, "Post rating type" + "properties.rating.format" = r#""double""#, "Post rating format" + } +} + +#[cfg(feature = "decimal_float")] +#[test] +fn derive_struct_with_rust_decimal_float_with_type_override() { + use rust_decimal::Decimal; + + let post = api_doc! { + struct Post { + id: i32, + #[schema(value_type = String)] + rating: Decimal, + } + }; + + assert_value! {post=> + "properties.id.type" = r#""integer""#, "Post id type" + "properties.id.format" = r#""int32""#, "Post id format" + "properties.rating.type" = r#""string""#, "Post rating type" + "properties.rating.format" = r#"null"#, "Post rating format" + } +} + #[cfg(feature = "uuid")] #[test] fn derive_struct_with_uuid_type() { diff --git a/utoipa/Cargo.toml b/utoipa/Cargo.toml index 503a07ef..3b063168 100644 --- a/utoipa/Cargo.toml +++ b/utoipa/Cargo.toml @@ -27,6 +27,7 @@ rocket_extras = ["utoipa-gen/rocket_extras"] axum_extras = ["utoipa-gen/axum_extras"] chrono = ["utoipa-gen/chrono"] decimal = ["utoipa-gen/decimal"] +decimal_float = ["utoipa-gen/decimal_float"] non_strict_integers = ["utoipa-gen/non_strict_integers"] yaml = ["serde_yaml", "utoipa-gen/yaml"] uuid = ["utoipa-gen/uuid"] diff --git a/utoipa/src/lib.rs b/utoipa/src/lib.rs index d8c8bfd9..7da55b2e 100644 --- a/utoipa/src/lib.rs +++ b/utoipa/src/lib.rs @@ -66,6 +66,9 @@ //! * **decimal** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** //! it is interpreted as `String`. If you wish to change the format you need to override the type. //! See the `value_type` in [`ToSchema` derive docs][to_schema_derive]. +//! * **decimal_float** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default** +//! it is interpreted as `Number`. This feature is mutually exclusive with **decimal** and allow to change the default type used in your +//! documentation for `Decimal` much like `serde_with_float` feature exposed by rust_decimal. //! * **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with //! format `uuid` in OpenAPI spec. //! * **ulid** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with