From 1b9c39b2e65f247f466ab24bc1caaecf2bc6be7f Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Wed, 13 Dec 2023 19:55:26 -0500 Subject: [PATCH] Path impl_for override. PathBuilder::path_from (#759) Add `impl_for = ...` attribute for `#[utoipa::path(...)]` macro. This allows users to use a custom type the `Path` trait will be implemented for instead of the default new type. --- utoipa-gen/src/lib.rs | 4 ++++ utoipa-gen/src/path.rs | 23 +++++++++++++++++------ utoipa/src/openapi/path.rs | 6 ++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs index 9f40583e..adf5153d 100644 --- a/utoipa-gen/src/lib.rs +++ b/utoipa-gen/src/lib.rs @@ -674,6 +674,10 @@ pub fn derive_to_schema(input: TokenStream) -> TokenStream { /// /// * `path = "..."` Must be OpenAPI format compatible str with arguments within curly braces. E.g _`{id}`_ /// +/// +/// * `impl_for = ...` Optional type to implement the [`Path`][path] trait. By default a new type +/// is used for the implementation. +/// /// * `operation_id = ...` Unique operation id for the endpoint. By default this is mapped to function name. /// The operation_id can be any valid expression (e.g. string literals, macro invocations, variables) so long /// as its result can be converted to a `String` using `String::from`. diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs index e68a4c48..495b3bfd 100644 --- a/utoipa-gen/src/path.rs +++ b/utoipa-gen/src/path.rs @@ -39,6 +39,7 @@ pub struct PathAttr<'p> { params: Vec>, security: Option>, context_path: Option, + impl_for: Option, } impl<'p> PathAttr<'p> { @@ -144,6 +145,10 @@ impl Parse for PathAttr<'_> { path_attr.context_path = Some(parse_utils::parse_next_literal_str_or_expr(input)?) } + "impl_for" => { + path_attr.impl_for = + Some(parse_utils::parse_next(input, || input.parse::())?); + } _ => { // any other case it is expected to be path operation if let Some(path_operation) = @@ -289,7 +294,6 @@ impl<'p> Path<'p> { impl<'p> ToTokens for Path<'p> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let path_struct = format_ident!("{}{}", PATH_STRUCT_PREFIX, self.fn_name); let operation_id = self .path_attr .operation_id @@ -385,13 +389,20 @@ impl<'p> ToTokens for Path<'p> { responses: self.path_attr.responses.as_ref(), security: self.path_attr.security.as_ref(), }; - + let impl_for = if let Some(impl_for) = &self.path_attr.impl_for { + impl_for.clone() + } else { + let path_struct = format_ident!("{}{}", PATH_STRUCT_PREFIX, self.fn_name); + tokens.extend(quote! { + #[allow(non_camel_case_types)] + #[doc(hidden)] + pub struct #path_struct; + }); + path_struct + }; tokens.extend(quote! { - #[allow(non_camel_case_types)] - #[doc(hidden)] - pub struct #path_struct; - impl utoipa::Path for #path_struct { + impl utoipa::Path for #impl_for { fn path() -> String { #path_with_context_path } diff --git a/utoipa/src/openapi/path.rs b/utoipa/src/openapi/path.rs index 5230cdd2..decd9416 100644 --- a/utoipa/src/openapi/path.rs +++ b/utoipa/src/openapi/path.rs @@ -3,6 +3,7 @@ //! [paths]: https://spec.openapis.org/oas/latest.html#paths-object use std::{collections::HashMap, iter}; +use crate::Path; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -107,6 +108,11 @@ impl PathsBuilder { pub fn extensions(mut self, extensions: Option>) -> Self { set_value!(self extensions extensions) } + /// Appends a [`Path`] to map of paths. By calling [`path`](PathsBuilder::path) method. + /// None will be passed into [Path::path_item] method. + pub fn path_from(self) -> Self { + self.path(P::path(), P::path_item(None)) + } } builder! {