diff --git a/Cargo.toml b/Cargo.toml index 8f5bc749..8afb45f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ actix-web-lab = "0.20" assert-json-diff = "2.0.2" convert_case = "0.6" darling = "0.20" -futures-core = "0.3.28" +futures-core = "0.3" +futures-util = "0.3" indexmap = "2" log = "0.4.20" num_cpus = "1.16" @@ -69,7 +70,6 @@ unused_import_braces = "warn" unused-lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" -unused_tuple_struct_fields = "warn" [workspace.lints.clippy] bool_to_int_with_if = "warn" diff --git a/apistos-gen-test/src/lib.rs b/apistos-gen-test/src/lib.rs index c2623feb..5b54c3ca 100644 --- a/apistos-gen-test/src/lib.rs +++ b/apistos-gen-test/src/lib.rs @@ -1,7 +1,6 @@ #![allow(deprecated)] #![allow(dead_code)] #![allow(clippy::expect_used)] -#![allow(unused_tuple_struct_fields)] #[cfg(test)] pub(crate) mod tests; diff --git a/apistos-gen-test/src/tests/api_error_derive.rs b/apistos-gen-test/src/tests/api_error_derive.rs index d048a371..ae3661dd 100644 --- a/apistos-gen-test/src/tests/api_error_derive.rs +++ b/apistos-gen-test/src/tests/api_error_derive.rs @@ -6,6 +6,7 @@ use std::collections::{BTreeMap, HashSet}; #[test] #[allow(dead_code)] fn api_component_derive() { + #[allow(clippy::duplicated_attributes)] #[derive(ApiErrorComponent)] #[openapi_error( status(code = 403), @@ -59,6 +60,7 @@ fn api_component_with_schema() { code: String, } + #[allow(clippy::duplicated_attributes)] #[derive(ApiErrorComponent)] #[openapi_error(status(code = 403), status(code = 409, description = "Too many requests"))] enum ErrorResponse { diff --git a/apistos-gen-test/src/tests/api_operation.rs b/apistos-gen-test/src/tests/api_operation.rs index eb35d166..a6daba27 100644 --- a/apistos-gen-test/src/tests/api_operation.rs +++ b/apistos-gen-test/src/tests/api_operation.rs @@ -74,6 +74,7 @@ mod test_models { } } + #[allow(clippy::duplicated_attributes)] #[derive(Serialize, Deserialize, Debug, Clone, ApiErrorComponent)] #[openapi_error(status(code = 401), status(code = 403), status(code = 404), status(code = 405))] pub(crate) enum MultipleErrorResponse { @@ -216,6 +217,20 @@ fn api_operation_impl_responder() { HttpResponse::Ok() } + #[allow(clippy::todo, clippy::unused_async)] + async fn plop() { + todo!() + } + + /// Add a new pet to the store + /// Add a new pet to the store + /// Plop + #[api_operation(tag = "pet")] + pub(crate) async fn test_async(_body: Json) -> impl Responder { + plop().await; + HttpResponse::Ok() + } + let components = __openapi_test::components(); // only one component here because: error does not have schema and Test is used both for query and response assert_eq!(components.len(), 1); @@ -267,6 +282,58 @@ fn api_operation_impl_responder() { ] }) ); + + let components = __openapi_test_async::components(); + // only one component here because: error does not have schema and Test is used both for query and response + assert_eq!(components.len(), 1); + let components = serde_json::to_value(components).expect("Unable to serialize as Json"); + + let operation = __openapi_test_async::operation(); + let operation = serde_json::to_value(operation).expect("Unable to serialize as Json"); + + assert_json_eq!( + components, + json!([ + { + "schemas": { + "Test": { + "properties": { + "test": { + "type": "string" + } + }, + "required": [ + "test" + ], + "title": "Test", + "type": "object" + } + } + } + ]) + ); + assert_json_eq!( + operation, + json!({ + "deprecated": false, + "description": "Add a new pet to the store\\\nPlop", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Test" + } + } + }, + "required": true + }, + "responses": {}, + "summary": "Add a new pet to the store", + "tags": [ + "pet" + ] + }) + ); } #[test] diff --git a/apistos-gen/src/internal/mod.rs b/apistos-gen/src/internal/mod.rs index 295e7808..606f77c1 100644 --- a/apistos-gen/src/internal/mod.rs +++ b/apistos-gen/src/internal/mod.rs @@ -186,7 +186,11 @@ pub(crate) fn gen_item_ast( let block = item_ast.block; let inner_handler = if is_responder { - quote!(core::future::ready(apistos::actix::ResponderWrapper((move || #block)()))) + quote!({ + use apistos::FutureExt; + #[expect(async_yields_async)] + (move || { async move #block })().map(apistos::actix::ResponderWrapper) + }) } else if is_impl_trait { quote!((move || #block)()) } else { diff --git a/apistos-gen/src/lib.rs b/apistos-gen/src/lib.rs index 6b0da233..5f150b3c 100644 --- a/apistos-gen/src/lib.rs +++ b/apistos-gen/src/lib.rs @@ -620,7 +620,7 @@ pub fn api_operation(attr: TokenStream, item: TokenStream) -> TokenStream { let operation_attribute = parse_openapi_operation_attrs(&attr_args); - let default_span = proc_macro2::Span::call_site(); + let default_span = Span::call_site(); let item_ast = match syn::parse::(item) { Ok(v) => v, Err(e) => abort!(e.span(), format!("{e}")), diff --git a/apistos-models/src/paths.rs b/apistos-models/src/paths.rs index 1578ae3a..593eaa61 100644 --- a/apistos-models/src/paths.rs +++ b/apistos-models/src/paths.rs @@ -309,7 +309,7 @@ pub struct Example { /// Long description for the example. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - /// Embedded literal example. The `value` field and `externalValue field are mutually exclusive. To represent examples of media types that cannot naturally represented in JSON or YAML, use a string value to contain the example, escaping where necessary. + /// Embedded literal example. The `value` field and `externalValue` field are mutually exclusive. To represent examples of media types that cannot naturally represented in JSON or YAML, use a string value to contain the example, escaping where necessary. #[serde(flatten)] pub value: ExampleValue, /// This object MAY be extended with [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specification-extensions). @@ -445,6 +445,6 @@ pub enum AnyOrExpression { pub enum OperationIdentifier { /// A relative or absolute URI reference to an OAS operation. This field is mutually exclusive of the `operationId` field, and MUST point to an [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object). Relative `operationRef` values MAY be used to locate an existing [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object) in the OpenAPI definition. OperationRef(String), - /// The name of an existing, resolvable OAS operation, as defined with a unique `operationId. This field is mutually exclusive of the `operationRef` field. + /// The name of an existing, resolvable OAS operation, as defined with a unique `operationId`. This field is mutually exclusive of the `operationRef` field. OperationId(String), } diff --git a/apistos-models/src/security.rs b/apistos-models/src/security.rs index a14a0657..47f1da9b 100644 --- a/apistos-models/src/security.rs +++ b/apistos-models/src/security.rs @@ -114,7 +114,7 @@ pub struct OauthImplicit { /// The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL. #[serde(skip_serializing_if = "Option::is_none")] pub refresh_url: Option, - /// The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it. The map MAY be empty. + /// The available scopes for the `OAuth2` security scheme. A map between the scope name and a short description for it. The map MAY be empty. pub scopes: BTreeMap, } @@ -127,7 +127,7 @@ pub struct OauthToken { /// The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL. #[serde(skip_serializing_if = "Option::is_none")] pub refresh_url: Option, - /// The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it. The map MAY be empty. + /// The available scopes for the `OAuth2` security scheme. A map between the scope name and a short description for it. The map MAY be empty. pub scopes: BTreeMap, } @@ -135,6 +135,6 @@ pub struct OauthToken { #[cfg_attr(any(test, feature = "deserialize"), derive(serde::Deserialize, PartialEq))] #[serde(rename_all = "camelCase")] pub struct OpenIdConnect { - /// OpenId Connect URL to discover OAuth2 configuration values. This MUST be in the form of a URL. + /// `OpenId` Connect URL to discover `OAuth2` configuration values. This MUST be in the form of a URL. pub open_id_connect_url: String, } diff --git a/apistos-shuttle/README.md b/apistos-shuttle/README.md index 65f7f010..f780a72b 100644 --- a/apistos-shuttle/README.md +++ b/apistos-shuttle/README.md @@ -75,7 +75,7 @@ async fn actix_web() -> ShuttleApistosActixWeb Default features are disabled for `shuttle-runtime` to avoid pulling [colored](https://github.com/colored-rs/colored) > which as a license considered as > copyleft by cargo-deny. -> To implement your own tracing, please visit https://docs.shuttle.rs/configuration/logs +> To implement your own tracing, please visit ### About us diff --git a/apistos/Cargo.toml b/apistos/Cargo.toml index 6fa003ee..bf797f8d 100644 --- a/apistos/Cargo.toml +++ b/apistos/Cargo.toml @@ -16,6 +16,7 @@ license.workspace = true [dependencies] actix-service = { workspace = true } actix-web = { workspace = true } +futures-util = { workspace = true } indexmap = { workspace = true } log = { workspace = true } md5 = { workspace = true } diff --git a/apistos/src/lib.rs b/apistos/src/lib.rs index 9efb24e8..a10343a8 100644 --- a/apistos/src/lib.rs +++ b/apistos/src/lib.rs @@ -163,6 +163,7 @@ pub use apistos_redoc::RedocConfig; pub use apistos_scalar::ScalarConfig; #[cfg(feature = "swagger-ui")] pub use apistos_swagger_ui::SwaggerUIConfig; +pub use futures_util::future::FutureExt; mod internal; diff --git a/apistos/tests/default_parameters.rs b/apistos/tests/default_parameters.rs index 508288fc..8f0113ca 100644 --- a/apistos/tests/default_parameters.rs +++ b/apistos/tests/default_parameters.rs @@ -50,7 +50,7 @@ async fn default_parameters() { plap: String, } - #[allow(unused_tuple_struct_fields)] + #[allow(dead_code)] #[derive(Clone, Debug, JsonSchema, ApiHeader)] #[openapi_header( name = "X-Env-Complex", @@ -59,7 +59,7 @@ async fn default_parameters() { )] struct SomeComplexHeader(TestHeaderStruct); - #[allow(unused_tuple_struct_fields)] + #[allow(dead_code)] #[derive(Clone, Debug, JsonSchema, ApiHeader)] #[openapi_header( name = "X-Env", @@ -182,6 +182,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/operation_id.rs b/apistos/tests/operation_id.rs index c68004cb..3e6fdc36 100644 --- a/apistos/tests/operation_id.rs +++ b/apistos/tests/operation_id.rs @@ -145,6 +145,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/operations.rs b/apistos/tests/operations.rs index 7ac22b82..44967be4 100644 --- a/apistos/tests/operations.rs +++ b/apistos/tests/operations.rs @@ -346,6 +346,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/parametric_handlers.rs b/apistos/tests/parametric_handlers.rs index 9b92b566..d1b61a63 100644 --- a/apistos/tests/parametric_handlers.rs +++ b/apistos/tests/parametric_handlers.rs @@ -187,6 +187,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/path_parameters.rs b/apistos/tests/path_parameters.rs index c0312119..0348cefa 100644 --- a/apistos/tests/path_parameters.rs +++ b/apistos/tests/path_parameters.rs @@ -137,6 +137,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/query_parameters.rs b/apistos/tests/query_parameters.rs index 9a5843bc..7fb462d1 100644 --- a/apistos/tests/query_parameters.rs +++ b/apistos/tests/query_parameters.rs @@ -281,6 +281,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use indexmap as _; use log as _; use md5 as _; diff --git a/apistos/tests/routing.rs b/apistos/tests/routing.rs index 90a4480b..cbf0730b 100644 --- a/apistos/tests/routing.rs +++ b/apistos/tests/routing.rs @@ -108,6 +108,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/apistos/tests/tags.rs b/apistos/tests/tags.rs index 99426b08..003d4ea2 100644 --- a/apistos/tests/tags.rs +++ b/apistos/tests/tags.rs @@ -149,6 +149,7 @@ use apistos_rapidoc as _; use apistos_redoc as _; use apistos_scalar as _; use apistos_swagger_ui as _; +use futures_util as _; use garde_actix_web as _; use indexmap as _; use log as _; diff --git a/rust-toolchain b/rust-toolchain index 7c7053aa..dbd41264 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.75.0 +1.81.0