Skip to content

Commit

Permalink
Allow additional integer types (#575)
Browse files Browse the repository at this point in the history
This PR adds a new `non_strict_integers` feature flag to allow use of non-standard integer formats added in #571 at OpenAPI spec.
  • Loading branch information
jayvdb authored Apr 15, 2023
1 parent 1abced1 commit dc0cf3c
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 31 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ and the `ipa` is _api_ reversed. Aaand... `ipa` is also an awesome type of beer
When disabled, the properties are listed in alphabetical order.
- **indexmap** Add support for [indexmap](https://crates.io/crates/indexmap). When enabled `IndexMap` will be rendered as a map similar to
`BTreeMap` and `HashMap`.
- **non_strict_integers** Add support for non-standard integer formats `int8`, `int16`, `uint8`, `uint16`, `uint32`, and `uint64`.

Utoipa implicitly has partial support for `serde` attributes. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html#partial-serde-attributes-support) for more details.

Expand Down
2 changes: 2 additions & 0 deletions utoipa-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ time = { version = "0.3", features = ["serde-human-readable"] }
serde_with = "2.3"

[features]
# See README.md for list and explanations of features
debug = ["syn/extra-traits"]
actix_extras = ["regex", "lazy_static", "syn/extra-traits"]
chrono = []
yaml = []
decimal = []
rocket_extras = ["regex", "lazy_static", "syn/extra-traits"]
non_strict_integers = []
uuid = ["dep:uuid"]
axum_extras = ["syn/extra-traits"]
time = []
Expand Down
21 changes: 19 additions & 2 deletions utoipa-gen/src/schema_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,27 @@ impl ToTokens for Type<'_> {
let name = &*last_segment.ident.to_string();

match name {
"i8" | "i16" | "i32" | "u8" | "u16" | "u32" => {
#[cfg(feature="non_strict_integers")]
"i8" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int8) }),
#[cfg(feature="non_strict_integers")]
"u8" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt8) }),
#[cfg(feature="non_strict_integers")]
"i16" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int16) }),
#[cfg(feature="non_strict_integers")]
"u16" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt16) }),
#[cfg(feature="non_strict_integers")]
#[cfg(feature="non_strict_integers")]
"u32" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt32) }),
#[cfg(feature="non_strict_integers")]
"u64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt64) }),
#[cfg(not(feature="non_strict_integers"))]
"i8" | "i16" | "u8" | "u16" | "u32" => {
tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int32) })
}
"i64" | "u64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64) }),
#[cfg(not(feature="non_strict_integers"))]
"u64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64) }),
"i32" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int32) }),
"i64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64) }),
"f32" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Float) }),
"f64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Double) }),
#[cfg(feature = "chrono")]
Expand Down
12 changes: 4 additions & 8 deletions utoipa-gen/tests/path_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ fn derive_path_with_extra_attributes_without_nested_module() {
status = 200, description = "success response")
),
params(
("id" = u64, deprecated = false, description = "Foo database id"),
("id" = i64, deprecated = false, description = "Foo database id"),
("since" = Option<String>, Query, deprecated = false, description = "Datetime since foo is updated")
)
)]
Expand Down Expand Up @@ -233,7 +233,6 @@ fn derive_path_with_extra_attributes_without_nested_module() {
"parameters.[0].in" = r#""path""#, "Parameter 0 in"
"parameters.[0].name" = r#""id""#, "Parameter 0 name"
"parameters.[0].required" = r#"true"#, "Parameter 0 required"
"parameters.[0].schema.minimum" = r#"0.0"#, "Parameter 0 minimum"
"parameters.[0].schema.format" = r#""int64""#, "Parameter 0 schema format"
"parameters.[0].schema.type" = r#""integer""#, "Parameter 0 schema type"

Expand Down Expand Up @@ -302,7 +301,7 @@ fn derive_path_with_parameter_schema() {
(status = 200, description = "success response")
),
params(
("id" = u64, description = "Foo database id"),
("id" = i64, description = "Foo database id"),
("since" = Option<Since>, Query, description = "Datetime since foo is updated")
)
)]
Expand Down Expand Up @@ -330,7 +329,6 @@ fn derive_path_with_parameter_schema() {
"schema": {
"format": "int64",
"type": "integer",
"minimum": 0.0,
}
},
{
Expand Down Expand Up @@ -373,7 +371,7 @@ fn derive_path_with_parameter_inline_schema() {
(status = 200, description = "success response")
),
params(
("id" = u64, description = "Foo database id"),
("id" = i64, description = "Foo database id"),
("since" = inline(Option<Since>), Query, description = "Datetime since foo is updated")
)
)]
Expand Down Expand Up @@ -401,7 +399,6 @@ fn derive_path_with_parameter_inline_schema() {
"schema": {
"format": "int64",
"type": "integer",
"minimum": 0.0,
}
},
{
Expand Down Expand Up @@ -954,7 +951,7 @@ fn derive_path_params_intoparams() {
/// Foo database id.
#[param(example = 1)]
#[allow(unused)]
id: u64,
id: i64,
/// Datetime since foo is updated.
#[param(example = "2020-04-12T10:23:00Z")]
#[allow(unused)]
Expand Down Expand Up @@ -1012,7 +1009,6 @@ fn derive_path_params_intoparams() {
"schema": {
"format": "int64",
"type": "integer",
"minimum": 0.0,
},
"style": "form"
},
Expand Down
2 changes: 1 addition & 1 deletion utoipa-gen/tests/path_parameter_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ mod mod_derive_parameters_all_types {
params(
("id" = i32, Path, description = "Foo id"),
("since" = String, Query, deprecated, description = "Datetime since"),
("numbers" = Option<[u64]>, Query, description = "Foo numbers list"),
("numbers" = Option<[i64]>, Query, description = "Foo numbers list"),
("token" = String, Header, deprecated, description = "Token of foo"),
("cookieval" = String, Cookie, deprecated, description = "Foo cookie"),
)
Expand Down
4 changes: 2 additions & 2 deletions utoipa-gen/tests/path_response_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,15 @@ object_body_with_multiple_headers => body: Foo, headers: (
"responses.200.headers.another-header.schema.type" = r###""string""###, "another-header header type"
"responses.200.headers.another-header.description" = r###"null"###, "another-header header description"
object_body_with_header_with_type => body: Foo, headers: (
("random-digits" = [u64]),
("random-digits" = [i64]),
), assert:
"responses.200.content.application~1json.schema.$ref" = r###""#/components/schemas/Foo""###, "Response content type"
"responses.200.headers.random-digits.schema.type" = r###""array""###, "random-digits header type"
"responses.200.headers.random-digits.description" = r###"null"###, "random-digits header description"
"responses.200.headers.random-digits.schema.items.type" = r###""integer""###, "random-digits header items type"
"responses.200.headers.random-digits.schema.items.format" = r###""int64""###, "random-digits header items format"
response_no_body_with_complex_header_with_description => headers: (
("random-digits" = [u64], description = "Random digits response header"),
("random-digits" = [i64], description = "Random digits response header"),
), assert:
"responses.200.content" = r###"null"###, "Response content type"
"responses.200.headers.random-digits.description" = r###""Random digits response header""###, "random-digits header description"
Expand Down
6 changes: 2 additions & 4 deletions utoipa-gen/tests/request_body_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ fn derive_request_body_primitive_simple_success() {

test_fn! {
module: derive_request_body_primitive_simple_array,
body: = [u64]
body: = [i64]
}

#[test]
Expand All @@ -154,7 +154,6 @@ fn derive_request_body_primitive_array_success() {
"items": {
"type": "integer",
"format": "int64",
"minimum": 0.0,
}
}
}
Expand Down Expand Up @@ -389,7 +388,7 @@ fn derive_request_body_complex_required_explicit_false_success() {

test_fn! {
module: derive_request_body_complex_primitive_array,
body: (content = [u32], description = "Create new foo references")
body: (content = [i32], description = "Create new foo references")
}

#[test]
Expand All @@ -412,7 +411,6 @@ fn derive_request_body_complex_primitive_array_success() {
"items": {
"type": "integer",
"format": "int32",
"minimum": 0.0,
}
}
}
Expand Down
62 changes: 58 additions & 4 deletions utoipa-gen/tests/schema_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ fn derive_struct_with_optional_properties() {
let owner = api_doc! {
struct Owner {
#[schema(default = 1)]
id: u64,
id: i64,
enabled: Option<bool>,
books: Option<Vec<Book>>,
metadata: Option<HashMap<String, String>>,
Expand All @@ -316,7 +316,6 @@ fn derive_struct_with_optional_properties() {
"type": "integer",
"format": "int64",
"default": 1,
"minimum": 0.0,
},
"enabled": {
"type": "boolean",
Expand Down Expand Up @@ -647,8 +646,8 @@ fn derive_unnamed_struct_deprecated_success() {
#[test]
fn derive_unnamed_struct_example_json_array_success() {
let pet_age = api_doc! {
#[schema(example = "0", default = u64::default)]
struct PetAge(u64, u64);
#[schema(example = "0", default = i64::default)]
struct PetAge(i64, i64);
};

assert_value! {pet_age=>
Expand Down Expand Up @@ -3811,6 +3810,61 @@ fn derive_struct_with_validation_fields() {
}
};

#[cfg(feature = "non_strict_integers")]
assert_json_eq!(
value,
json!({
"properties": {
"id": {
"format": "int32",
"type": "integer",
"maximum": 10.0,
"minimum": 5.0,
"multipleOf": 2.5,
},
"value": {
"type": "string",
"maxLength": 10,
"minLength": 5,
"pattern": "[a-z]*"
},
"items": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
},
"maxItems": 5,
"minItems": 1,
},
"unsigned": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"unsigned_value": {
"type": "integer",
"format": "uint32",
"minimum": 2.0,
}
"unsigned_value": {
"type": "integer",
"format": "int32",
"minimum": 2.0,
}
},
"type": "object",
"required": [
"id",
"value",
"items",
"unsigned",
"unsigned_value"
]
})
);

#[cfg(not(feature = "non_strict_integers"))]
assert_json_eq!(
value,
json!({
Expand Down
4 changes: 3 additions & 1 deletion utoipa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ categories = ["web-programming"]
authors = ["Juha Kukkonen <[email protected]>"]

[features]
# See README.md for list and explanations of features
default = []
debug = ["utoipa-gen/debug"]
actix_extras = ["utoipa-gen/actix_extras"]
rocket_extras = ["utoipa-gen/rocket_extras"]
axum_extras = ["utoipa-gen/axum_extras"]
chrono = ["utoipa-gen/chrono"]
decimal = ["utoipa-gen/decimal"]
non_strict_integers = ["utoipa-gen/non_strict_integers"]
yaml = ["serde_yaml", "utoipa-gen/yaml"]
uuid = ["utoipa-gen/uuid"]
time = ["utoipa-gen/time"]
Expand All @@ -46,5 +48,5 @@ indexmap = { version = "1", features = ["serde"] }
assert-json-diff = "2"

[package.metadata.docs.rs]
features = ["actix_extras", "openapi_extensions", "yaml", "uuid"]
features = ["actix_extras", "non_strict_integers", "openapi_extensions", "uuid", "yaml"]
rustdoc-args = ["--cfg", "doc_cfg"]
Loading

0 comments on commit dc0cf3c

Please sign in to comment.