Repository: juhaku/utoipa
Pull request #29 (2022-02-16 21:51:54)

Enhance the tag & default tag support
* Remove Tag and DefaultTag traits along with the need for those traits
* Remove useless assert struct
* Make path to be more neutral about its location -> allows path macro attributed methods within methods where as previously they needed to exists only in module level


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index edc9f90..c87f462 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -235,13 +235,5 @@ pub trait Component {
 pub trait Path {
     fn path() -> &'static str;
 
-    fn path_item() -> openapi::path::PathItem;
-}
-
-pub trait DefaultTag {
-    fn tag() -> &'static str;
-}
-
-pub trait Tag {
-    fn tag() -> &'static str;
+    fn path_item(defalt_tag: Option<&str>) -> openapi::path::PathItem;
 }
diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs
index 4ce12d3..2a4f0bd 100644
--- a/utoipa-gen/src/lib.rs
+++ b/utoipa-gen/src/lib.rs
@@ -1,4 +1,8 @@
-//! This is private utoipa codegen library and is not used alone
+//! This is **private** utoipa codegen library and is not used alone
+//!
+//! The library contains macro implementations for utoipa library. Content
+//! of the libarary documentation is available through **utoipa** library itself.
+//! Consider browsing via the **utoipa** crate so all links will work correctly.
 
 #![warn(missing_docs)]
 #![warn(rustdoc::broken_intra_doc_links)]
diff --git a/utoipa-gen/src/openapi.rs b/utoipa-gen/src/openapi.rs
index 13bd0af..6f221a2 100644
--- a/utoipa-gen/src/openapi.rs
+++ b/utoipa-gen/src/openapi.rs
@@ -172,7 +172,7 @@ impl ToTokens for OpenApi {
             },
         );
 
-        let path_items = impl_paths(&attributes.handlers, tokens);
+        let path_items = impl_paths(&attributes.handlers);
 
         tokens.extend(quote! {
             impl utoipa::OpenApi for #ident {
@@ -186,14 +186,14 @@ impl ToTokens for OpenApi {
     }
 }
 
-fn impl_paths(handler_paths: &[ExprPath], quote: &mut TokenStream) -> TokenStream {
+fn impl_paths(handler_paths: &[ExprPath]) -> TokenStream {
     handler_paths.iter().fold(
         quote! { utoipa::openapi::path::Paths::new() },
         |mut paths, handler| {
             let segments = handler.path.segments.iter().collect::<Vec<_>>();
             let handler_fn_name = &*segments.last().unwrap().ident.to_string();
 
-            let tag = segments
+            let tag = &*segments
                 .iter()
                 .take(segments.len() - 1)
                 .map(|part| part.ident.to_string())
@@ -205,12 +205,7 @@ fn impl_paths(handler_paths: &[ExprPath], quote: &mut TokenStream) -> TokenStrea
 
             let usage = syn::parse_str::<ExprPath>(
                 &vec![
-                    if tag.starts_with("crate") {
-                        None
-                    } else {
-                        Some("crate")
-                    },
-                    if tag.is_empty() { None } else { Some(&tag) },
+                    if tag.is_empty() { None } else { Some(tag) },
                     Some(handler_ident_name),
                 ]
                 .into_iter()
@@ -220,19 +215,8 @@ fn impl_paths(handler_paths: &[ExprPath], quote: &mut TokenStream) -> TokenStrea
             )
             .unwrap();
 
-            let assert_handler_ident = format_ident!("__assert_{}", handler_ident_name);
-            quote.extend(quote! {
-                #[allow(non_camel_case_types)]
-                struct #assert_handler_ident where #handler_ident : utoipa::Path;
-                use #usage;
-                impl utoipa::DefaultTag for #handler_ident {
-                    fn tag() -> &'static str {
-                        #tag
-                    }
-                }
-            });
             paths.extend(quote! {
-                .append(#handler_ident::path(), #handler_ident::path_item())
+                .append(#usage::path(), #usage::path_item(Some(#tag)))
             });
 
             paths
diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs
index a721be2..5a89e2d 100644
--- a/utoipa-gen/src/path.rs
+++ b/utoipa-gen/src/path.rs
@@ -355,7 +355,7 @@ impl ToTokens for Path {
             .as_ref()
             .or(Some(&self.fn_name))
             .expect_or_abort("expected to find operation id but was None");
-        let tag = self
+        let tag = &*self
             .path_attr
             .tag
             .as_ref()
@@ -375,7 +375,6 @@ impl ToTokens for Path {
             .expect_or_abort("expected to find path but was None");
 
         let operation = Operation {
-            path_struct: &path_struct,
             deprecated: &self.deprecated,
             operation_id,
             summary: self
@@ -392,22 +391,19 @@ impl ToTokens for Path {
             #[allow(non_camel_case_types)]
             pub struct #path_struct;
 
-            impl utoipa::Tag for #path_struct {
-                fn tag() -> &'static str {
-                    #tag
-                }
-            }
-
             impl utoipa::Path for #path_struct {
                 fn path() -> &'static str {
                     #path
                 }
 
-                fn path_item() -> utoipa::openapi::path::PathItem {
+                fn path_item(default_tag: Option<&str>) -> utoipa::openapi::path::PathItem {
                     use utoipa::openapi::ToArray;
                     utoipa::openapi::PathItem::new(
                         #path_operation,
-                        #operation
+                        #operation.with_tag(*[Some(#tag), default_tag, Some("crate")].iter()
+                            .flatten()
+                            .find(|t| !t.is_empty()).unwrap()
+                        )
                     )
                 }
             }
@@ -416,7 +412,6 @@ impl ToTokens for Path {
 }
 
 struct Operation<'a> {
-    path_struct: &'a Ident,
     operation_id: &'a String,
     summary: Option<&'a String>,
     description: Option<&'a Vec<String>>,
@@ -441,15 +436,8 @@ impl ToTokens for Operation<'_> {
             .with_responses(#responses)
         });
         //         // .with_security()
-        let path_struct = self.path_struct;
         let operation_id = self.operation_id;
         tokens.extend(quote! {
-            .with_tag(
-                [<#path_struct as utoipa::Tag>::tag(),
-                    <#path_struct as utoipa::DefaultTag>::tag()
-                ]
-                .into_iter().find(|s| !s.is_empty()).unwrap_or_else(|| "crate")
-            )
             .with_operation_id(
                 #operation_id
             )


Test patch:
diff --git a/tests/path_derive.rs b/tests/path_derive.rs
index e6020b4..bf4ad1f 100644
--- a/tests/path_derive.rs
+++ b/tests/path_derive.rs
@@ -123,15 +123,14 @@ fn derive_path_with_all_info_success() {
     }
 }
 
-test_api_fn! {
-    name: test_operation3,
-    module: derive_path_with_defaults,
-    operation: post,
-    path: "/foo/bar";
-}
-
 #[test]
 fn derive_path_with_defaults_success() {
+    test_api_fn! {
+        name: test_operation3,
+        module: derive_path_with_defaults,
+        operation: post,
+        path: "/foo/bar";
+    }
     let operation = test_api_fn_doc! {
         derive_path_with_defaults::test_operation3,
         operation: post,
diff --git a/tests/utoipa_gen_test.rs b/tests/utoipa_gen_test.rs
index ee4b6b0..ceda215 100644
--- a/tests/utoipa_gen_test.rs
+++ b/tests/utoipa_gen_test.rs
@@ -1,8 +1,6 @@
 #![cfg(feature = "actix_extras")]
 #![allow(dead_code)]
 
-use std::borrow::Cow;
-
 use serde::{Deserialize, Serialize};
 use utoipa::{Component, OpenApi};