Repository: juhaku/utoipa
Pull request #11 (2022-01-17 23:36:00)

Refactor and organize code2
* Refactor and organized code structure 


Patch:
diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs
index 51284a4..fd5e9f7 100644
--- a/utoipa-gen/src/component.rs
+++ b/utoipa-gen/src/component.rs
@@ -4,47 +4,69 @@ use proc_macro2::{Ident, TokenStream as TokenStream2};
 use proc_macro_error::{abort, abort_call_site, emit_error};
 use quote::{quote, ToTokens};
 use syn::{
-    Attribute, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, PathArguments, PathSegment,
-    Type, TypePath, Variant,
+    Attribute, Data, Field, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, PathArguments,
+    PathSegment, Type, TypePath, Variant,
 };
 
 use crate::{
-    attribute::{parse_component_attribute, AttributeType, CommentAttributes, ComponentAttribute},
     component_type::{ComponentFormat, ComponentType},
+    doc_comment::CommentAttributes,
     ValueArray,
 };
 
-pub(crate) fn impl_component(data: syn::Data, attrs: Vec<syn::Attribute>) -> TokenStream2 {
-    let component = ComponentVariant::new(data, &attrs);
+use self::attribute::{AttributeType, ComponentAttribute};
 
-    quote! {
-        use utoipa::openapi::{ComponentType, ComponentFormat};
+mod attribute;
 
-        #component.into()
+pub struct Component<'a> {
+    ident: &'a Ident,
+    variant: ComponentVariant<'a>,
+}
+
+impl<'a> Component<'a> {
+    pub fn new(data: Data, attributes: &'a [Attribute], ident: &'a Ident) -> Self {
+        Self {
+            ident,
+            variant: ComponentVariant::new(data, attributes),
+        }
     }
 }
 
-#[cfg_attr(feature = "debug", derive(Debug))]
+impl ToTokens for Component<'_> {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        let ident = self.ident;
+        let component_variant_impl = &self.variant;
+
+        tokens.extend(quote! {
+            impl utoipa::Component for #ident {
+                fn component() -> utoipa::openapi::schema::Component {
+                    #component_variant_impl.into()
+                }
+            }
+        })
+    }
+}
 
-enum FieldType {
+#[cfg_attr(feature = "debug", derive(Debug))]
+pub enum FieldType {
     Named,
     Unnamed,
 }
 
 #[cfg_attr(feature = "debug", derive(Debug))]
 /// Holds the OpenAPI Component implementation which can be added the Schema.
-enum ComponentVariant<'a> {
+pub enum ComponentVariant<'a> {
     /// Object variant is rust sturct with Component derive annotation.
-    Object(Vec<syn::Field>, &'a [Attribute], FieldType),
+    Object(Vec<Field>, &'a [Attribute], FieldType),
     /// Enum variant is rust enum with Component derive annotation. **Only supports** enums with
     /// Unit type fields.
-    Enum(Vec<syn::Variant>, &'a [Attribute]),
+    Enum(Vec<Variant>, &'a [Attribute]),
 }
 
 impl<'a> ComponentVariant<'a> {
-    fn new(data: syn::Data, attributes: &'a [Attribute]) -> ComponentVariant<'a> {
+    pub fn new(data: Data, attributes: &'a [Attribute]) -> ComponentVariant<'a> {
         match data {
-            syn::Data::Struct(content) => {
+            Data::Struct(content) => {
                 let (fields , field_type ) = match content.fields {
                     Fields::Unnamed(fields) => {
                         let FieldsUnnamed { unnamed, .. } = fields;
@@ -58,7 +80,7 @@ impl<'a> ComponentVariant<'a> {
                 };
                 ComponentVariant::Object(fields.into_iter().collect(), attributes, field_type)
             }
-            syn::Data::Enum(content) => {
+            Data::Enum(content) => {
                 ComponentVariant::Enum(content.variants.into_iter().collect(), attributes)
             }
             _ => abort_call_site!(
@@ -70,6 +92,10 @@ impl<'a> ComponentVariant<'a> {
 
 impl<'a> ToTokens for ComponentVariant<'a> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
+        tokens.extend(quote! {
+            use utoipa::openapi::{ComponentType, ComponentFormat};
+        });
+
         match self {
             Self::Object(fields, attrs, field_type) => {
                 self.struct_to_tokens(fields, *attrs, tokens, field_type)
@@ -82,7 +108,7 @@ impl<'a> ToTokens for ComponentVariant<'a> {
 impl<'a> ComponentVariant<'a> {
     fn struct_to_tokens(
         &self,
-        fields: &[syn::Field],
+        fields: &[Field],
         attributes: &[Attribute],
         tokens: &mut TokenStream2,
         field_type: &FieldType,
@@ -95,7 +121,7 @@ impl<'a> ComponentVariant<'a> {
         self.append_description(attributes, tokens);
     }
 
-    fn named_fields_struct_to_tokens(&self, fields: &[syn::Field], tokens: &mut TokenStream2) {
+    fn named_fields_struct_to_tokens(&self, fields: &[Field], tokens: &mut TokenStream2) {
         tokens.extend(quote! { utoipa::openapi::Object::new() });
 
         fields.iter().for_each(|field| {
@@ -103,14 +129,13 @@ impl<'a> ComponentVariant<'a> {
 
             let component_part = &ComponentPart::from_type(&field.ty);
             let component = Into::<ComponentPartRef<'_, ComponentPart<'_>>>::into(component_part)
-                .collect::<Component>();
+                .collect::<ComponentPropertyType>();
 
             let property = create_property_stream(
                 &component,
                 CommentAttributes::from_attributes(&field.attrs),
-                parse_component_attribute(&field.attrs),
+                attribute::parse_component_attribute(&field.attrs),
             );
-
             tokens.extend(quote! {
                 .with_property(#field_name, #property)
             });
@@ -123,17 +148,17 @@ impl<'a> ComponentVariant<'a> {
         });
     }
 
-    fn unnamed_fields_struct_to_tokens(&self, fields: &[syn::Field], tokens: &mut TokenStream2) {
+    fn unnamed_fields_struct_to_tokens(&self, fields: &[Field], tokens: &mut TokenStream2) {
         let fields_len = fields.len();
         let first_field = fields.first().unwrap();
         let first_part = &ComponentPart::from_type(&first_field.ty);
         let first_component = Into::<ComponentPartRef<'_, ComponentPart<'_>>>::into(first_part)
-            .collect::<Component>();
+            .collect::<ComponentPropertyType>();
 
         let all_fields_are_same = fields.iter().skip(1).all(|field| {
             let component_part = &ComponentPart::from_type(&field.ty);
             let component = Into::<ComponentPartRef<'_, ComponentPart<'_>>>::into(component_part)
-                .collect::<Component>();
+                .collect::<ComponentPropertyType>();
 
             first_component == component
         });
@@ -192,7 +217,7 @@ impl<'a> ComponentVariant<'a> {
                 .with_enum_values(#enum_values)
         });
 
-        if let Some(enum_attributes) = parse_component_attribute(attributes) {
+        if let Some(enum_attributes) = attribute::parse_component_attribute(attributes) {
             append_attributes(
                 tokens,
                 enum_attributes
@@ -234,12 +259,12 @@ fn append_attributes<I: Iterator<Item = AttributeType>>(
 }
 
 fn create_property_stream(
-    component: &Component,
+    component: &ComponentPropertyType,
     comment_attributes: CommentAttributes,
     component_attribute: Option<ComponentAttribute>,
 ) -> TokenStream2 {
     let mut property = match component {
-        Component {
+        ComponentPropertyType {
             generic_type: None,
             value_type:
                 Some(TypeTuple(
@@ -248,7 +273,7 @@ fn create_property_stream(
                 )),
             ..
         } => create_simple_property(value_type, ident, &comment_attributes),
-        Component {
+        ComponentPropertyType {
             generic_type: Some(generic_type_tuple),
             value_type: Some(value_type_tuple),
             ..
@@ -491,13 +516,13 @@ struct TypeTuple<'a, T>(T, &'a Ident);
 
 #[cfg_attr(feature = "debug", derive(Debug))]
 #[derive(Default, PartialEq)]
-struct Component<'a> {
+struct ComponentPropertyType<'a> {
     option: bool,
     generic_type: Option<TypeTuple<'a, GenericType>>,
     value_type: Option<TypeTuple<'a, ValueType>>,
 }
 
-impl<'a> FromIterator<ComponentPartRef<'a, ComponentPart<'a>>> for Component<'a> {
+impl<'a> FromIterator<ComponentPartRef<'a, ComponentPart<'a>>> for ComponentPropertyType<'a> {
     fn from_iter<T: IntoIterator<Item = ComponentPartRef<'a, ComponentPart<'a>>>>(iter: T) -> Self {
         let components_iter = iter.into_iter();
         components_iter.fold(Self::default(), |mut component, item| {
diff --git a/utoipa-gen/src/attribute.rs b/utoipa-gen/src/component/attribute.rs
similarity index 73%
rename from utoipa-gen/src/attribute.rs
rename to utoipa-gen/src/component/attribute.rs
index b443623..13b0c16 100644
--- a/utoipa-gen/src/attribute.rs
+++ b/utoipa-gen/src/component/attribute.rs
@@ -1,63 +1,13 @@
+use std::{slice::Iter, vec::IntoIter};
+
 use proc_macro2::{Ident, Span};
-use proc_macro_error::{abort, abort_call_site, emit_warning, OptionExt, ResultExt};
+use proc_macro_error::{abort, emit_warning, OptionExt, ResultExt};
 use quote::{quote_spanned, ToTokens};
-use syn::{parse::Parse, Attribute, ExprPath, Lit, Meta, Token};
-
-const DOC_ATTRIBUTE_TYPE: &str = "doc";
-const COMPONENT_ATTRIBUTE_TYPE: &str = "component";
+use syn::{parse::Parse, Attribute, ExprPath, Lit, Token};
 
-/// CommentAttributes holds Vec of parsed doc comments
-pub(crate) struct CommentAttributes(pub(crate) Vec<String>);
-
-impl CommentAttributes {
-    pub(crate) fn empty() -> Self {
-        Self { 0: vec![] }
-    }
-    /// Creates new [`CommentAttributes`] instance from syn::Attribute slice filtering out all
-    /// other attributes which are not `doc` comments
-    pub(crate) fn from_attributes(attributes: &[Attribute]) -> Self {
-        Self {
-            0: Self::as_string_vec(attributes.iter().filter(Self::is_doc_attribute)),
-        }
-    }
-
-    fn is_doc_attribute(attribute: &&Attribute) -> bool {
-        &*Self::get_attribute_ident(attribute).to_string() == DOC_ATTRIBUTE_TYPE
-    }
-
-    fn get_attribute_ident(attribute: &Attribute) -> &Ident {
-        attribute
-            .path
-            .get_ident()
-            .expect_or_abort("Expected doc attribute with one path segment")
-    }
+use crate::doc_comment::CommentAttributes;
 
-    fn as_string_vec<'a, I: Iterator<Item = &'a Attribute>>(attributes: I) -> Vec<String> {
-        attributes
-            .into_iter()
-            .filter_map(Self::parse_doc_comment)
-            .collect()
-    }
-
-    fn parse_doc_comment(attribute: &Attribute) -> Option<String> {
-        let meta = attribute.parse_meta().unwrap_or_abort();
-
-        match meta {
-            Meta::NameValue(name_value) => {
-                if let syn::Lit::Str(doc_comment) = name_value.lit {
-                    Some(doc_comment.value().trim().to_string())
-                } else {
-                    emit_warning!(
-                        Span::call_site(),
-                        "Expected Lit::Str types for types in meta, ignoring value"
-                    );
-                    None
-                }
-            }
-            _ => abort_call_site!("Exected only Meta::NameValue type"),
-        }
-    }
-}
+const COMPONENT_ATTRIBUTE_TYPE: &str = "component";
 
 #[cfg_attr(feature = "debug", derive(Debug))]
 /// AttributeType is parsed representation of `#[component(...)]` attribute values of Component derive.
diff --git a/utoipa-gen/src/component_experiment.rs b/utoipa-gen/src/component_experiment.rs
new file mode 100644
index 0000000..fba1568
--- /dev/null
+++ b/utoipa-gen/src/component_experiment.rs
@@ -0,0 +1,90 @@
+
+struct Foo<'a> {
+    value_type: &'a TypeTuple<'a, ValueType>,
+    description: Option<&'a String>,
+    generic: Option<&'a GenericType>,
+    component_attributes: Option<&'a ComponentAttribute>,
+}
+
+impl ToTokens for Foo<'_> {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        match self.generic {
+            Some(GenericType::Map) => {
+                tokens.extend(quote! {
+                    utoipa::openapi::Object::new()
+                });
+                if let Some(ref description) = self.description {
+                    tokens.extend(quote! {
+                        .with_description(#description)
+                    })
+                }
+            },
+            Some(GenericType::Vec) => {
+                let f = Foo {
+                    description: self.description,
+                    generic: None, // TODO this needs some refactoring need support for nested generics
+                    value_type: self.value_type,
+                    component_attributes: self.component_attributes,
+                };
+                
+                quote! {
+                    #f.to_array()
+                };
+            },
+            None => {
+                let TypeTuple(value, ident) = self.value_type;
+                
+                match value {
+                    ValueType::Primitive => {
+                        let component_type = ComponentType(ident);
+
+                        tokens.extend(quote! {
+                            utoipa::openapi::Property::new(
+                                #component_type
+                            )
+                        });
+
+                        if let Some(ref description) = self.description {
+                            tokens.extend(quote! {
+                                .with_description(#description)
+                            })
+                        }
+
+                        let format = ComponentFormat(ident);
+                        if format.is_known_format() {
+                            tokens.extend(quote! {
+                                .with_format(#format)
+                            })
+                        }
+                    }
+                    ValueType::Object => {
+                        let object_name = &*ident.to_string();
+
+                        tokens.extend(quote! {
+                            utoipa::openapi::Ref::from_component_name(#object_name)
+                        })
+                    }
+                }
+            },
+            _ => unreachable!("Some(GenericType::Option) is not valid scneario optionality is checked otherwise!!!")
+        }
+
+        if let Some(component_attribute) = self.component_attributes {
+            component_attribute
+                .0
+                .iter()
+                .map(|attribute_type| match attribute_type {
+                    AttributeType::Default(..) => quote! {
+                        .with_default(#attribute_type)
+                    },
+                    AttributeType::Example(..) => quote! {
+                        .with_example(#attribute_type)
+                    },
+                    AttributeType::Format(..) => quote! {
+                        .with_format(#attribute_type)
+                    },
+                })
+                .for_each(|stream| tokens.extend(stream))
+        }
+    }
+}
\ No newline at end of file
diff --git a/utoipa-gen/src/doc_comment.rs b/utoipa-gen/src/doc_comment.rs
new file mode 100644
index 0000000..d7ac994
--- /dev/null
+++ b/utoipa-gen/src/doc_comment.rs
@@ -0,0 +1,59 @@
+use proc_macro2::{Ident, Span};
+use proc_macro_error::{abort_call_site, emit_warning, OptionExt, ResultExt};
+use syn::{Attribute, Lit, Meta};
+
+const DOC_ATTRIBUTE_TYPE: &str = "doc";
+
+/// CommentAttributes holds Vec of parsed doc comments
+pub(crate) struct CommentAttributes(pub(crate) Vec<String>);
+
+impl CommentAttributes {
+    pub(crate) fn empty() -> Self {
+        Self { 0: vec![] }
+    }
+    /// Creates new [`CommentAttributes`] instance from [`Attribute`] slice filtering out all
+    /// other attributes which are not `doc` comments
+    pub(crate) fn from_attributes(attributes: &[Attribute]) -> Self {
+        Self {
+            0: Self::as_string_vec(attributes.iter().filter(Self::is_doc_attribute)),
+        }
+    }
+
+    fn is_doc_attribute(attribute: &&Attribute) -> bool {
+        &*Self::get_attribute_ident(attribute).to_string() == DOC_ATTRIBUTE_TYPE
+    }
+
+    fn get_attribute_ident(attribute: &Attribute) -> &Ident {
+        attribute
+            .path
+            .get_ident()
+            .expect_or_abort("Expected doc attribute with one path segment")
+    }
+
+    fn as_string_vec<'a, I: Iterator<Item = &'a Attribute>>(attributes: I) -> Vec<String> {
+        attributes
+            .into_iter()
+            .filter_map(Self::parse_doc_comment)
+            .collect()
+    }
+
+    fn parse_doc_comment(attribute: &Attribute) -> Option<String> {
+        let meta = attribute.parse_meta().unwrap_or_abort();
+
+        // TODO find a correct span?
+        match meta {
+            Meta::NameValue(name_value) => {
+                if let Lit::Str(doc_comment) = name_value.lit {
+                    Some(doc_comment.value().trim().to_string())
+                } else {
+                    emit_warning!(
+                        Span::call_site(),
+                        "Expected Lit::Str types for types in meta, ignoring value"
+                    );
+                    None
+                }
+            }
+            _ => abort_call_site!("Exected only Meta::NameValue type"),
+        }
+    }
+}
diff --git a/utoipa-gen/src/ext/mod.rs b/utoipa-gen/src/ext.rs
similarity index 100%
rename from utoipa-gen/src/ext/mod.rs
rename to utoipa-gen/src/ext.rs
diff --git a/utoipa-gen/src/ext/actix.rs b/utoipa-gen/src/ext/actix.rs
index 2b8aee2..8d7bba4 100644
--- a/utoipa-gen/src/ext/actix.rs
+++ b/utoipa-gen/src/ext/actix.rs
@@ -10,7 +10,6 @@ use syn::{
 use super::{
     Argument, ArgumentIn, ArgumentResolver, PathOperationResolver, PathOperations, PathResolver,
 };
-use crate::path::{Parameter, ParameterIn};
 
 #[cfg(feature = "actix_extras")]
 impl ArgumentResolver for PathOperations {
@@ -204,59 +203,3 @@ fn is_valid_request_type(s: &str) -> bool {
         _ => false,
     }
 }
-
-#[inline]
-#[cfg(feature = "actix_extras")]
-pub fn update_parameters_from_arguments(
-    arguments: Option<Vec<Argument>>,
-    parameters: &mut Option<Vec<Parameter>>,
-) {
-    if let Some(arguments) = arguments {
-        let new_parameter = |argument: &Argument| {
-            Parameter::new(
-                &argument.name.as_ref().unwrap(),
-                argument.ident,
-                if argument.argument_in == ArgumentIn::Path {
-                    ParameterIn::Path
-                } else {
-                    ParameterIn::Query
-                },
-            )
-        };
-
-        if let Some(ref mut parameters) = parameters {
-            parameters.iter_mut().for_each(|parameter| {
-                if let Some(argument) = arguments
-                    .iter()
-                    .find(|argument| argument.name.as_ref() == Some(&parameter.name))
-                {
-                    parameter.update_parameter_type(argument.ident)
-                }
-            });
-
-            // add argument to the parameters if argument has a name and it does not exists in parameters
-            arguments
-                .iter()
-                .filter(|argument| argument.has_name())
-                .for_each(|argument| {
-                    // cannot use filter() for mutli borrow situation. :(
-                    if !parameters
-                        .iter()
-                        .any(|parameter| Some(&parameter.name) == argument.name.as_ref())
-                    {
-                        // if parameters does not contain argument
-                        parameters.push(new_parameter(argument))
-                    }
-                });
-        } else {
-            // no parameters at all, add arguments to the parameters if argument has a name
-            let mut params = Vec::with_capacity(arguments.len());
-            arguments
-                .iter()
-                .filter(|argument| argument.has_name())
-                .map(new_parameter)
-                .for_each(|parameter| params.push(parameter));
-            *parameters = Some(params);
-        }
-    }
-}
diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs
index 180e401..de5de27 100644
--- a/utoipa-gen/src/lib.rs
+++ b/utoipa-gen/src/lib.rs
@@ -3,41 +3,31 @@
 #![warn(missing_docs)]
 #![warn(rustdoc::broken_intra_doc_links)]
 
-#[cfg(feature = "actix_extras")]
-use ext::actix::update_parameters_from_arguments;
+use component::Component;
+use doc_comment::CommentAttributes;
 
 use ext::{ArgumentResolver, PathOperationResolver, PathOperations, PathResolver};
+use openapi::OpenApi;
 use proc_macro::TokenStream;
-use quote::{format_ident, quote, quote_spanned, ToTokens, TokenStreamExt};
+use proc_macro_error::{proc_macro_error, OptionExt, ResultExt};
+use quote::{quote, ToTokens, TokenStreamExt};
 
 use proc_macro2::{Group, Ident, Punct, TokenStream as TokenStream2};
 use syn::{
     bracketed,
-    parse::{Parse, ParseStream},
+    parse::{Parse, ParseBuffer, ParseStream},
     punctuated::Punctuated,
-    token::Bracket,
-    Attribute, DeriveInput, ExprPath, LitStr, Token,
+    DeriveInput, Error,
 };
 
-mod attribute;
 mod component;
 mod component_type;
+mod doc_comment;
 mod ext;
-mod info;
+mod openapi;
 mod path;
-mod property;
-mod request_body;
-mod response;
 
-use proc_macro_error::*;
-
-use crate::{
-    attribute::CommentAttributes,
-    component::impl_component,
-    path::{Path, PathAttr, PathOperation},
-};
-
-const PATH_STRUCT_PREFIX: &str = "__path_";
+use crate::path::{Path, PathAttr, PathOperation};
 
 #[proc_macro_error]
 #[proc_macro_derive(Component, attributes(component))]
@@ -47,17 +37,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
         attrs, ident, data, ..
     } = syn::parse_macro_input!(input);
 
-    let component_quote = impl_component(data, attrs);
+    let component = Component::new(data, &attrs, &ident);
 
-    let component = quote! {
-        impl utoipa::Component for #ident {
-            fn component() -> utoipa::openapi::schema::Component {
-                #component_quote
-            }
-        }
-    };
-
-    component.into()
+    quote! {
+        #component
+    }
+    .into()
 }
 
 #[proc_macro_error]
@@ -71,7 +56,7 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream {
     let arguments = PathOperations::resolve_path_arguments(&ast_fn.sig.inputs);
 
     #[cfg(feature = "actix_extras")]
-    update_parameters_from_arguments(arguments, &mut path_attribute.params);
+    path_attribute.update_parameters(arguments);
 
     let operation_attribute = &PathOperations::resolve_attribute(&ast_fn);
     let path_provider = || PathOperations::resolve_path(operation_attribute);
@@ -102,238 +87,20 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream {
 
 #[proc_macro_error]
 #[proc_macro_derive(OpenApi, attributes(openapi))]
+/// Derive OpenApi macro
 pub fn openapi(input: TokenStream) -> TokenStream {
-    let DeriveInput {
-        attrs,
-        // data,
-        // generics,
-        ident,
-        ..
-    } = syn::parse_macro_input!(input);
-
-    let openapi_args =
-        parse_openapi_attributes(&attrs).expect_or_abort("Expected #openapi[...] attribute");
-
-    // let files = openapi_args
-    //     .iter()
-    //     .filter(|args| matches!(args, OpenApiArgs::HandlerFiles(_)))
-    //     .flat_map(|args| match args {
-    //         OpenApiArgs::HandlerFiles(files) => files,
-    //         _ => unreachable!(),
-    //     });
-
-    let components = openapi_args
-        .iter()
-        .filter(|args| matches!(args, OpenApiArgs::Components(_)))
-        .flat_map(|args| match args {
-            OpenApiArgs::Components(components) => components,
-            _ => unreachable!(),
-            // TODO enabed if argument resolving is enabled
-        })
-        .collect::<Vec<_>>();
-
-    let info = info::impl_info();
-    // let paths = paths::impl_paths(&files.map(String::to_owned).collect::<Vec<_>>());
-
-    let span = ident.span();
-    let mut quote = quote! {};
-    let mut schema = quote! {
-        utoipa::openapi::Schema::new()
-    };
-
-    let handlers = openapi_args
-        .iter()
-        .filter_map(|args| match args {
-            OpenApiArgs::Handlers(handlers) => Some(handlers.clone()),
-            _ => None,
-        })
-        .flatten()
-        .collect::<Vec<_>>();
-
-    // println!("handlers: {:#?}", &handlers);
-
-    let path_items = impl_paths(handlers.into_iter(), &mut quote);
-
-    for component in components {
-        let component_name = &*component.to_string();
-        let assert_ident = format_ident!("_AssertComponent{}", component_name);
-        quote.extend(quote_spanned! {span=>
-            struct #assert_ident where #component: utoipa::Component;
-        });
-
-        schema.extend(quote! {
-            .with_component(#component_name, #component::component())
-        });
-    }
-
-    quote.extend(quote! {
-        use utoipa::openapi::schema::ToArray;
-        impl utoipa::OpenApi for #ident {
-            fn openapi() -> utoipa::openapi::OpenApi {
-                utoipa::openapi::OpenApi::new(#info, #path_items)
-                    .with_components(#schema)
-            }
-        }
-    });
+    let DeriveInput { attrs, ident, .. } = syn::parse_macro_input!(input);
 
-    quote.into()
-}
-
-fn parse_openapi_attributes(attributes: &[Attribute]) -> Option<Vec<OpenApiArgs>> {
-    if attributes.len() > 1 {
-        panic!(
-            "Expected at most 1 attribute, but found: {}",
-            &attributes.len()
+    let openapi_attributes = openapi::parse_openapi_attributes_from_attributes(&attrs)
+        .expect_or_abort(
+            "expected #[openapi(...)] attribute to be present when used with OpenApi derive trait",
         );
-    }
-
-    attributes
-        .iter()
-        .next()
-        .map(|attribute| {
-            if !attribute.path.is_ident("openapi") {
-                abort_call_site!("Expected #[openapi(...)]");
-            } else {
-                attribute
-            }
-        })
-        .map(|att| {
-            att.parse_args_with(Punctuated::<OpenApiArgs, Token![,]>::parse_terminated)
-                .unwrap_or_abort()
-                .into_iter()
-                .collect()
-        })
-}
 
-#[cfg_attr(feature = "debug", derive(Debug))]
-enum OpenApiArgs {
-    HandlerFiles(Vec<String>),
-    Components(Vec<Ident>),
-    Handlers(Vec<syn::ExprPath>),
-}
-
-impl Parse for OpenApiArgs {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let name_ident = input.parse::<Ident>()?;
-        let name_str = &*name_ident.to_string();
-
-        match name_str {
-            "handler_files" => {
-                if input.peek(Token![=]) {
-                    input.parse::<Token![=]>()?;
-                }
-
-                if input.peek(syn::token::Bracket) {
-                    let content;
-                    bracketed!(content in input);
-                    let tokens = Punctuated::<LitStr, Token![,]>::parse_terminated(&content)?;
-
-                    Ok(Self::HandlerFiles(
-                        tokens.iter().map(LitStr::value).collect::<Vec<_>>(),
-                    ))
-                } else {
-                    Err(syn::Error::new(
-                        input.span(),
-                        "Expected handler_files = [...]",
-                    ))
-                }
-            }
-            "components" => {
-                if input.peek(Token![=]) {
-                    input.parse::<Token![=]>()?;
-                }
-
-                if input.peek(syn::token::Bracket) {
-                    let content;
-                    bracketed!(content in input);
-                    let tokens = Punctuated::<Ident, Token![,]>::parse_terminated(&content)?;
-
-                    Ok(Self::Components(tokens.into_iter().collect::<Vec<_>>()))
-                } else {
-                    Err(syn::Error::new(input.span(), "Expected components = [...]"))
-                }
-            }
-            "handlers" => {
-                if input.peek(Token![=]) {
-                    input.parse::<Token![=]>()?;
-                }
-
-                if input.peek(syn::token::Bracket) {
-                    let content;
-                    bracketed!(content in input);
-                    let tokens =
-                        Punctuated::<syn::ExprPath, Token![,]>::parse_terminated(&content)?;
-
-                    Ok(Self::Handlers(tokens.into_iter().collect::<Vec<_>>()))
-                } else {
-                    Err(syn::Error::new(input.span(), "Expected handlers = [...]"))
-                }
-            }
-            _ => Err(syn::Error::new(
-                input.span(),
-                "unexpected token expected either handler_files or components",
-            )),
-        }
+    let openapi = OpenApi(openapi_attributes, ident);
+    quote! {
+        #openapi
     }
-}
-
-fn impl_paths<I: IntoIterator<Item = ExprPath>>(
-    handler_paths: I,
-    quote: &mut TokenStream2,
-) -> TokenStream2 {
-    quote.extend(quote! {
-        use utoipa::Path as OpenApiPath;
-    });
-    handler_paths.into_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
-                .iter()
-                .take(segments.len() - 1)
-                .map(|part| part.ident.to_string())
-                .collect::<Vec<_>>()
-                .join("::");
-
-            let handler_ident = format_ident!("{}{}", PATH_STRUCT_PREFIX, handler_fn_name);
-            let handler_ident_name = &*handler_ident.to_string();
-
-            let usage = syn::parse_str::<ExprPath>(
-                &vec![
-                    if tag.starts_with("crate") {
-                        None
-                    } else {
-                        Some("crate")
-                    },
-                    if tag.is_empty() { None } else { Some(&tag) },
-                    Some(handler_ident_name),
-                ]
-                .into_iter()
-                .flatten()
-                .collect::<Vec<_>>()
-                .join("::"),
-            )
-            .unwrap();
-
-            let assert_handler_ident = format_ident!("__assert_{}", handler_ident_name);
-            quote.extend(quote! {
-                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())
-            });
-
-            paths
-        },
-    )
+    .into()
 }
 
 /// Tokenizes slice or Vec of tokenizable items as slice reference (`&[...]`) correctly to OpenAPI JSON.
@@ -423,28 +190,80 @@ impl ToTokens for Required {
 }
 
 /// Media type is wrapper around type and information is type an array
-#[derive(Default)]
+// #[derive(Default)]
 #[cfg_attr(feature = "debug", derive(Debug))]
 struct MediaType {
-    ty: Option<Ident>,
+    ty: Ident,
     is_array: bool,
 }
 
+impl MediaType {
+    pub fn new(ident: Ident) -> Self {
+        Self {
+            ty: ident,
+            is_array: false,
+        }
+    }
+}
+
 impl Parse for MediaType {
     fn parse(input: ParseStream) -> syn::Result<Self> {
         let mut is_array = false;
-        let ty = if input.peek(Bracket) {
+
+        let parse_ident = |group: &ParseBuffer, error_msg: &str| {
+            if group.peek(syn::Ident) {
+                group.parse::<Ident>()
+            } else {
+                Err(Error::new(input.span(), error_msg))
+            }
+        };
+
+        let ty = if input.peek(syn::Ident) {
+            parse_ident(input, "unparseable MediaType, expected Ident")
+        } else {
             is_array = true;
+
             let group;
             bracketed!(group in input);
-            group.parse::<Ident>().unwrap()
-        } else {
-            input.parse::<Ident>().unwrap()
-        };
 
-        Ok(MediaType {
-            ty: Some(ty),
-            is_array,
+            parse_ident(
+                &group,
+                "unparseable MediaType, expected Ident within Bracket Group",
+            )
+        }?;
+
+        Ok(MediaType { ty, is_array })
+    }
+}
+
+/// Parsing utils
+mod parse_utils {
+    use proc_macro_error::ResultExt;
+    use syn::{parse::ParseStream, LitBool, LitStr, Token};
+
+    pub fn parse_next<T: Sized>(input: ParseStream, next: impl FnOnce() -> T) -> T {
+        input
+            .parse::<Token![=]>()
+            .expect_or_abort("expected equals token (=) before value assigment");
+        next()
+    }
+
+    pub fn parse_next_lit_str(input: ParseStream, error_message: &str) -> String {
+        parse_next(input, || {
+            input
+                .parse::<LitStr>()
+                .expect_or_abort(error_message)
+                .value()
         })
     }
+
+    pub fn parse_bool_or_true(input: ParseStream) -> bool {
+        if input.peek(Token![=]) && input.peek2(LitBool) {
+            input.parse::<Token![=]>().unwrap();
+
+            input.parse::<LitBool>().unwrap().value()
+        } else {
+            true
+        }
+    }
 }
diff --git a/utoipa-gen/src/openapi.rs b/utoipa-gen/src/openapi.rs
new file mode 100644
index 0000000..3e9b5d0
--- /dev/null
+++ b/utoipa-gen/src/openapi.rs
@@ -0,0 +1,202 @@
+use proc_macro2::Ident;
+use proc_macro_error::ResultExt;
+use syn::{
+    bracketed,
+    parse::{Parse, ParseStream},
+    punctuated::Punctuated,
+    Attribute, Error, ExprPath, Token,
+};
+
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote, quote_spanned, ToTokens};
+
+use crate::parse_utils;
+
+mod info;
+
+const PATH_STRUCT_PREFIX: &str = "__path_";
+
+#[derive(Default)]
+#[cfg_attr(feature = "debug", derive(Debug))]
+pub struct OpenApiAttr {
+    handlers: Vec<ExprPath>,
+    components: Vec<Ident>,
+}
+
+pub fn parse_openapi_attributes_from_attributes(attrs: &[Attribute]) -> Option<OpenApiAttr> {
+    attrs
+        .iter()
+        .find(|attribute| attribute.path.is_ident("openapi"))
+        .map(|attribute| attribute.parse_args::<OpenApiAttr>().unwrap_or_abort())
+}
+
+impl Parse for OpenApiAttr {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let mut openapi = OpenApiAttr::default();
+
+        loop {
+            let ident = input
+                .parse::<Ident>()
+                .expect_or_abort("unaparseable OpenApi, expected Ident");
+            let attribute = &*ident.to_string();
+
+            match attribute {
+                "handlers" => {
+                    openapi.handlers = parse_handlers(input).unwrap_or_abort();
+                }
+                "components" => {
+                    openapi.components = parse_components(input).unwrap_or_abort();
+                }
+                _ => {
+                    return Err(Error::new(
+                        ident.span(),
+                        format!(
+                            "unexpected attribute: {}, expected: handlers, components",
+                            ident
+                        ),
+                    ));
+                }
+            }
+
+            if input.peek(Token![,]) {
+                input.parse::<Token![,]>().unwrap_or_abort();
+            }
+            if input.is_empty() {
+                break;
+            }
+        }
+
+        Ok(openapi)
+    }
+}
+
+fn parse_handlers(input: ParseStream) -> syn::Result<Vec<ExprPath>> {
+    parse_utils::parse_next(input, || {
+        if input.peek(syn::token::Bracket) {
+            let content;
+            bracketed!(content in input);
+            let tokens = Punctuated::<ExprPath, Token![,]>::parse_terminated(&content)?;
+
+            Ok(tokens.into_iter().collect::<Vec<_>>())
+        } else {
+            Err(Error::new(
+                input.span(),
+                "unparseable handlers, expected Bracket Token [...]",
+            ))
+        }
+    })
+}
+
+fn parse_components(input: ParseStream) -> syn::Result<Vec<Ident>> {
+    parse_utils::parse_next(input, || {
+        if input.peek(syn::token::Bracket) {
+            let content;
+            bracketed!(content in input);
+            let tokens = Punctuated::<Ident, Token![,]>::parse_terminated(&content)?;
+
+            Ok(tokens.into_iter().collect::<Vec<_>>())
+        } else {
+            Err(syn::Error::new(
+                input.span(),
+                "unparseable components, expected Bracket Token [...]",
+            ))
+        }
+    })
+}
+
+pub(crate) struct OpenApi(pub OpenApiAttr, pub Ident);
+
+impl ToTokens for OpenApi {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        let OpenApi(attributes, ident) = self;
+
+        let info = info::impl_info();
+
+        let schema = attributes.components.iter().fold(
+            quote! { utoipa::openapi::Schema::new() },
+            |mut schema, component| {
+                let span = component.span();
+                let component_name = &*component.to_string();
+
+                let assert_component = format_ident!("_AssertComponent{}", component_name);
+                tokens.extend(quote_spanned! {span=>
+                    struct #assert_component where #component: utoipa::Component;
+                });
+
+                schema.extend(quote! {
+                    .with_component(#component_name, #component::component())
+                });
+
+                schema
+            },
+        );
+
+        let path_items = impl_paths(&attributes.handlers, tokens);
+
+        tokens.extend(quote! {
+            use utoipa::openapi::schema::ToArray;
+            impl utoipa::OpenApi for #ident {
+                fn openapi() -> utoipa::openapi::OpenApi {
+                    utoipa::openapi::OpenApi::new(#info, #path_items)
+                        .with_components(#schema)
+                }
+            }
+        });
+    }
+}
+
+fn impl_paths(handler_paths: &[ExprPath], quote: &mut TokenStream) -> TokenStream {
+    quote.extend(quote! {
+        use utoipa::Path as OpenApiPath;
+    });
+    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
+                .iter()
+                .take(segments.len() - 1)
+                .map(|part| part.ident.to_string())
+                .collect::<Vec<_>>()
+                .join("::");
+
+            let handler_ident = format_ident!("{}{}", PATH_STRUCT_PREFIX, handler_fn_name);
+            let handler_ident_name = &*handler_ident.to_string();
+
+            let usage = syn::parse_str::<ExprPath>(
+                &vec![
+                    if tag.starts_with("crate") {
+                        None
+                    } else {
+                        Some("crate")
+                    },
+                    if tag.is_empty() { None } else { Some(&tag) },
+                    Some(handler_ident_name),
+                ]
+                .into_iter()
+                .flatten()
+                .collect::<Vec<_>>()
+                .join("::"),
+            )
+            .unwrap();
+
+            let assert_handler_ident = format_ident!("__assert_{}", handler_ident_name);
+            quote.extend(quote! {
+                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())
+            });
+
+            paths
+        },
+    )
+}
diff --git a/utoipa-gen/src/info.rs b/utoipa-gen/src/openapi/info.rs
similarity index 98%
rename from utoipa-gen/src/info.rs
rename to utoipa-gen/src/openapi/info.rs
index 562a9bd..08c4dab 100644
--- a/utoipa-gen/src/info.rs
+++ b/utoipa-gen/src/openapi/info.rs
@@ -10,14 +10,12 @@ pub(crate) fn impl_info() -> TokenStream2 {
 
     let contact = get_contact(&authors);
 
-    let info = quote! {
+    quote! {
         utoipa::openapi::Info::new(#name, #version)
             .with_description(#description)
             .with_licence(utoipa::openapi::Licence::new(#licence))
             .with_contact(#contact)
-    };
-
-    info
+    }
 }
 
 fn get_parsed_author(author: Option<&str>) -> Option<(&str, String)> {
diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs
index 1e86d11..bb081e9 100644
--- a/utoipa-gen/src/path.rs
+++ b/utoipa-gen/src/path.rs
@@ -1,6 +1,6 @@
 use std::{io::Error, str::FromStr};
 
-use proc_macro2::{Group, Ident, Span, TokenStream as TokenStream2};
+use proc_macro2::{Group, Ident, TokenStream as TokenStream2};
 use proc_macro_error::{abort_call_site, OptionExt, ResultExt};
 use quote::{format_ident, quote, ToTokens};
 use syn::{
@@ -8,17 +8,26 @@ use syn::{
     parse::{Parse, ParseStream},
     parse2,
     punctuated::Punctuated,
-    token::Bracket,
-    LitBool, LitInt, LitStr, Token,
+    LitStr, Token,
 };
 
+use crate::Deprecated;
 use crate::{
-    component_type::{ComponentFormat, ComponentType},
+    component_type::ComponentType,
+    ext::{Argument, ArgumentIn},
+};
+
+use self::{
+    parameter::{Parameter, ParameterIn},
     request_body::RequestBodyAttr,
     response::{Response, Responses},
-    Deprecated, Required,
 };
 
+pub mod parameter;
+mod property;
+mod request_body;
+mod response;
+
 const PATH_STRUCT_PREFIX: &str = "__path_";
 
 /// PathAttr is parsed `#[utoipa::path(...)]` proc macro and its attributes.
@@ -60,7 +69,61 @@ pub struct PathAttr {
     path: Option<String>,
     operation_id: Option<String>,
     tag: Option<String>,
-    pub params: Option<Vec<Parameter>>,
+    params: Option<Vec<Parameter>>,
+}
+
+impl PathAttr {
+    #[cfg(feature = "actix_extras")]
+    pub fn update_parameters(&mut self, arguments: Option<Vec<Argument>>) {
+        if let Some(arguments) = arguments {
+            let new_parameter = |argument: &Argument| {
+                Parameter::new(
+                    &argument.name.as_ref().unwrap(),
+                    argument.ident,
+                    if argument.argument_in == ArgumentIn::Path {
+                        ParameterIn::Path
+                    } else {
+                        ParameterIn::Query
+                    },
+                )
+            };
+
+            if let Some(ref mut parameters) = self.params {
+                parameters.iter_mut().for_each(|parameter| {
+                    if let Some(argument) = arguments
+                        .iter()
+                        .find(|argument| argument.name.as_ref() == Some(&parameter.name))
+                    {
+                        parameter.update_parameter_type(argument.ident)
+                    }
+                });
+
+                // add argument to the parameters if argument has a name and it does not exists in parameters
+                arguments
+                    .iter()
+                    .filter(|argument| argument.has_name())
+                    .for_each(|argument| {
+                        // cannot use filter() for mutli borrow situation. :(
+                        if !parameters
+                            .iter()
+                            .any(|parameter| Some(&parameter.name) == argument.name.as_ref())
+                        {
+                            // if parameters does not contain argument
+                            parameters.push(new_parameter(argument))
+                        }
+                    });
+            } else {
+                // no parameters at all, add arguments to the parameters if argument has a name
+                let mut params = Vec::with_capacity(arguments.len());
+                arguments
+                    .iter()
+                    .filter(|argument| argument.has_name())
+                    .map(new_parameter)
+                    .for_each(|parameter| params.push(parameter));
+                self.params = Some(params);
+            }
+        }
+    }
 }
 
 impl Parse for PathAttr {
@@ -240,210 +303,6 @@ impl ToTokens for PathOperation {
         tokens.extend(path_item_type);
     }
 }
-
-/// Parameter of request suchs as in path, header, query or cookie
-///
-/// For example path `/users/{id}` the path parameter is used to define
-/// type, format and other details of the `{id}` parameter within the path
-///
-/// Parse is executed for following formats:
-///
-/// * ("id" = String, path, required, deprecated, description = "Users database id"),
-/// * ("id", path, required, deprecated, description = "Users database id"),
-///
-/// The `= String` type statement is optional if automatic resolvation is supported.
-#[derive(Default)]
-#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct Parameter {
-    pub name: String,
-    parameter_in: ParameterIn,
-    required: bool,
-    deprecated: bool,
-    pub parameter_type: Option<Ident>,
-    is_array: bool,
-    description: Option<String>,
-}
-
-impl Parameter {
-    pub fn new<S: AsRef<str>>(name: S, parameter_type: &Ident, parameter_in: ParameterIn) -> Self {
-        let required = parameter_in == ParameterIn::Path;
-
-        Self {
-            name: name.as_ref().to_string(),
-            parameter_type: Some(parameter_type.clone()),
-            parameter_in,
-            required,
-            ..Default::default()
-        }
-    }
-
-    pub fn update_parameter_type(&mut self, ident: &Ident) {
-        self.parameter_type = Some(ident.clone());
-    }
-}
-
-impl Parse for Parameter {
-    fn parse(input: ParseStream) -> syn::Result<Self> {
-        let mut parameter = Parameter::default();
-
-        if input.peek(LitStr) {
-            // parse name
-            let name = input.parse::<LitStr>().unwrap().value();
-            parameter.name = name;
-            if input.peek(Token![=]) && input.peek2(syn::Ident) {
-                // parse type for name if provided
-                input.parse::<Token![=]>().unwrap();
-                parameter.parameter_type = Some(input.parse::<syn::Ident>().unwrap());
-            } else if input.peek(Token![=]) && input.peek2(Bracket) {
-                // parse group as array
-                input.parse::<Token![=]>().unwrap();
-                parameter.is_array = true;
-                let group_content;
-                bracketed!(group_content in input);
-                parameter.parameter_type = Some(group_content.parse::<Ident>().unwrap());
-            }
-        } else {
-            return Err(input.error("expected first element to be LitStr parameter name"));
-        }
-        if input.peek(Token![,]) {
-            input.parse::<Token![,]>().unwrap();
-        }
-
-        let parse_bool = |input: ParseStream| {
-            // support assign form as: required = bool
-            if input.peek(Token![=]) && input.peek2(LitBool) {
-                input.parse::<Token![=]>().unwrap();
-
-                input.parse::<LitBool>().unwrap().value()
-            } else {
-                // quick form as: required
-                true
-            }
-        };
-
-        loop {
-            let ident = input.parse::<syn::Ident>().unwrap();
-            let name = &*ident.to_string();
-            match name {
-                "path" | "query" | "header" | "cookie" => {
-                    parameter.parameter_in = name.parse::<ParameterIn>().unwrap_or_abort();
-                    if parameter.parameter_in == ParameterIn::Path {
-                        parameter.required = true; // all path parameters are required by default
-                    }
-                }
-                "required" => parameter.required = parse_bool(input),
-                "deprecated" => parameter.deprecated = parse_bool(input),
-                "description" => {
-                    if input.peek(Token![=]) {
-                        input.parse::<Token![=]>().unwrap();
-                    }
-
-                    parameter.description = Some(
-                        input
-                            .parse::<LitStr>()
-                            .expect_or_abort("expected description value as LitStr")
-                            .value(),
-                    )
-                }
-                _ => return Err(input.error(&format!("unexpected element: {}", name))),
-            }
-
-            if input.peek(Token![,]) {
-                input.parse::<Token![,]>().unwrap();
-            }
-            if input.is_empty() {
-                break;
-            }
-        }
-        Ok(parameter)
-    }
-}
-impl ToTokens for Parameter {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        let name = &*self.name;
-        tokens.extend(quote! { utoipa::openapi::path::Parameter::new(#name) });
-        let parameter_in = &self.parameter_in;
-        tokens.extend(quote! { .with_in(#parameter_in) });
-
-        let required: Required = self.required.into();
-        tokens.extend(quote! { .with_required(#required) });
-
-        let deprecated: Deprecated = self.deprecated.into();
-        tokens.extend(quote! { .with_deprecated(#deprecated) });
-
-        if let Some(ref description) = self.description {
-            tokens.extend(quote! { .with_description(#description) });
-        }
-
-        if let Some(ref parameter_type) = self.parameter_type {
-            // TODO unify this property logic with the one in component.rs
-            let component_type = ComponentType(parameter_type);
-            let mut property = quote! {
-                utoipa::openapi::Property::new(
-                    #component_type
-                )
-            };
-            let format = ComponentFormat(parameter_type);
-            if format.is_known_format() {
-                property.extend(quote! {
-                    .with_format(#format)
-                })
-            }
-            if self.is_array {
-                property.extend(quote! { .to_array() });
-            }
-
-            tokens.extend(quote! { .with_schema(#property) });
-        }
-    }
-}
-
-#[cfg_attr(feature = "debug", derive(Debug))]
-#[derive(PartialEq)]
-pub enum ParameterIn {
-    Query,
-    Path,
-    Header,
-    Cookie,
-}
-
-impl Default for ParameterIn {
-    fn default() -> Self {
-        Self::Path
-    }
-}
-
-impl FromStr for ParameterIn {
-    type Err = syn::Error;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "path" => Ok(Self::Path),
-            "query" => Ok(Self::Query),
-            "header" => Ok(Self::Header),
-            "cookie" => Ok(Self::Cookie),
-            _ => Err(syn::Error::new(
-                Span::call_site(),
-                &format!(
-                    "unexpected str: {}, expected one of: path, query, header, cookie",
-                    s
-                ),
-            )),
-        }
-    }
-}
-
-impl ToTokens for ParameterIn {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        tokens.extend(match self {
-            Self::Path => quote! { utoipa::openapi::path::ParameterIn::Path },
-            Self::Query => quote! { utoipa::openapi::path::ParameterIn::Query },
-            Self::Header => quote! { utoipa::openapi::path::ParameterIn::Header },
-            Self::Cookie => quote! { utoipa::openapi::path::ParameterIn::Cookie },
-        })
-    }
-}
-
 pub struct Path {
     path_attr: PathAttr,
     fn_name: String,
@@ -519,7 +378,7 @@ impl ToTokens for Path {
             .expect_or_abort("expected to find path but was None");
 
         let operation = Operation {
-            fn_name: &self.fn_name,
+            path_struct: &path_struct,
             deprecated: &self.deprecated,
             operation_id,
             summary: self
@@ -559,7 +418,7 @@ impl ToTokens for Path {
 }
 
 struct Operation<'a> {
-    fn_name: &'a String,
+    path_struct: &'a Ident,
     operation_id: &'a String,
     summary: Option<&'a String>,
     description: Option<&'a Vec<String>>,
@@ -584,7 +443,7 @@ impl ToTokens for Operation<'_> {
             .with_responses(#responses)
         });
         //         // .with_security()
-        let path_struct = format_ident!("{}{}", PATH_STRUCT_PREFIX, self.fn_name);
+        let path_struct = self.path_struct;
         let operation_id = self.operation_id;
         tokens.extend(quote! {
             .with_tag(
@@ -632,3 +491,19 @@ impl ToTokens for Operation<'_> {
         }
     }
 }
+
+trait ContentTypeResolver {
+    fn resolve_content_type<'a>(
+        &self,
+        content_type: Option<&'a String>,
+        component_type: &ComponentType<'a>,
+    ) -> &'a str {
+        if let Some(content_type) = content_type {
+            content_type
+        } else if component_type.is_primitive() {
+            "text/plain"
+        } else {
+            "application/json"
+        }
+    }
+}
diff --git a/utoipa-gen/src/path/parameter.rs b/utoipa-gen/src/path/parameter.rs
new file mode 100644
index 0000000..be18942
--- /dev/null
+++ b/utoipa-gen/src/path/parameter.rs
@@ -0,0 +1,187 @@
+use std::str::FromStr;
+
+use proc_macro2::{Ident, Span, TokenStream};
+use proc_macro_error::ResultExt;
+use quote::{quote, ToTokens};
+use syn::{
+    parse::{Parse, ParseStream},
+    LitStr, Token,
+};
+
+use crate::{parse_utils, Deprecated, MediaType, Required};
+
+use super::property::Property;
+
+/// Parameter of request suchs as in path, header, query or cookie
+///
+/// For example path `/users/{id}` the path parameter is used to define
+/// type, format and other details of the `{id}` parameter within the path
+///
+/// Parse is executed for following formats:
+///
+/// * ("id" = String, path, required, deprecated, description = "Users database id"),
+/// * ("id", path, required, deprecated, description = "Users database id"),
+///
+/// The `= String` type statement is optional if automatic resolvation is supported.
+#[derive(Default)]
+#[cfg_attr(feature = "debug", derive(Debug))]
+pub struct Parameter {
+    pub name: String,
+    parameter_in: ParameterIn,
+    required: bool,
+    deprecated: bool,
+    description: Option<String>,
+    parameter_type: Option<MediaType>,
+}
+
+impl Parameter {
+    pub fn new<S: AsRef<str>>(name: S, parameter_type: &Ident, parameter_in: ParameterIn) -> Self {
+        let required = parameter_in == ParameterIn::Path;
+
+        Self {
+            name: name.as_ref().to_string(),
+            parameter_type: Some(MediaType::new(parameter_type.clone())),
+            parameter_in,
+            required,
+            ..Default::default()
+        }
+    }
+
+    pub fn update_parameter_type(&mut self, ident: &Ident) {
+        self.parameter_type = Some(MediaType::new(ident.clone()));
+    }
+}
+
+impl Parse for Parameter {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let mut parameter = Parameter::default();
+
+        if input.peek(LitStr) {
+            // parse name
+            let name = input.parse::<LitStr>().unwrap().value();
+            parameter.name = name;
+
+            if input.peek(Token![=]) {
+                parameter.parameter_type =
+                    parse_utils::parse_next(input, || {
+                        Some(input.parse().expect_or_abort(
+                            "unparseable parameter type expected: Ident or [Ident]",
+                        ))
+                    });
+            }
+        } else {
+            return Err(input.error("expected first element to be LitStr parameter name"));
+        }
+        if input.peek(Token![,]) {
+            input.parse::<Token![,]>().unwrap();
+        }
+
+        loop {
+            let ident = input.parse::<syn::Ident>().unwrap();
+            let name = &*ident.to_string();
+            match name {
+                "path" | "query" | "header" | "cookie" => {
+                    parameter.parameter_in = name.parse::<ParameterIn>().unwrap_or_abort();
+                    if parameter.parameter_in == ParameterIn::Path {
+                        parameter.required = true; // all path parameters are required by default
+                    }
+                }
+                "required" => parameter.required = parse_utils::parse_bool_or_true(input),
+                "deprecated" => parameter.deprecated = parse_utils::parse_bool_or_true(input),
+                "description" => {
+                    if input.peek(Token![=]) {
+                        input.parse::<Token![=]>().unwrap();
+                    }
+
+                    parameter.description = Some(
+                        ResultExt::expect_or_abort(
+                            input.parse::<LitStr>(),
+                            "expected description value as LitStr",
+                        )
+                        .value(),
+                    )
+                }
+                _ => return Err(input.error(&format!("unexpected element: {}", name))),
+            }
+
+            if input.peek(Token![,]) {
+                input.parse::<Token![,]>().unwrap();
+            }
+            if input.is_empty() {
+                break;
+            }
+        }
+        Ok(parameter)
+    }
+}
+
+impl ToTokens for Parameter {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let name = &*self.name;
+        tokens.extend(quote! { utoipa::openapi::path::Parameter::new(#name) });
+        let parameter_in = &self.parameter_in;
+        tokens.extend(quote! { .with_in(#parameter_in) });
+
+        let required: Required = self.required.into();
+        tokens.extend(quote! { .with_required(#required) });
+
+        let deprecated: Deprecated = self.deprecated.into();
+        tokens.extend(quote! { .with_deprecated(#deprecated) });
+
+        if let Some(ref description) = self.description {
+            tokens.extend(quote! { .with_description(#description) });
+        }
+
+        if let Some(ref parameter_type) = self.parameter_type {
+            let property = Property::new(parameter_type.is_array, &parameter_type.ty);
+
+            tokens.extend(quote! { .with_schema(#property) });
+        }
+    }
+}
+
+#[cfg_attr(feature = "debug", derive(Debug))]
+#[derive(PartialEq)]
+pub enum ParameterIn {
+    Query,
+    Path,
+    Header,
+    Cookie,
+}
+
+impl Default for ParameterIn {
+    fn default() -> Self {
+        Self::Path
+    }
+}
+
+impl FromStr for ParameterIn {
+    type Err = syn::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "path" => Ok(Self::Path),
+            "query" => Ok(Self::Query),
+            "header" => Ok(Self::Header),
+            "cookie" => Ok(Self::Cookie),
+            _ => Err(syn::Error::new(
+                Span::call_site(),
+                &format!(
+                    "unexpected str: {}, expected one of: path, query, header, cookie",
+                    s
+                ),
+            )),
+        }
+    }
+}
+
+impl ToTokens for ParameterIn {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        tokens.extend(match self {
+            Self::Path => quote! { utoipa::openapi::path::ParameterIn::Path },
+            Self::Query => quote! { utoipa::openapi::path::ParameterIn::Query },
+            Self::Header => quote! { utoipa::openapi::path::ParameterIn::Header },
+            Self::Cookie => quote! { utoipa::openapi::path::ParameterIn::Cookie },
+        })
+    }
+}
diff --git a/utoipa-gen/src/property.rs b/utoipa-gen/src/path/property.rs
similarity index 100%
rename from utoipa-gen/src/property.rs
rename to utoipa-gen/src/path/property.rs
diff --git a/utoipa-gen/src/request_body.rs b/utoipa-gen/src/path/request_body.rs
similarity index 66%
rename from utoipa-gen/src/request_body.rs
rename to utoipa-gen/src/path/request_body.rs
index 3a9bb23..143a998 100644
--- a/utoipa-gen/src/request_body.rs
+++ b/utoipa-gen/src/path/request_body.rs
@@ -2,12 +2,14 @@ use proc_macro2::{Ident, TokenStream as TokenStream2};
 use quote::{quote, ToTokens};
 use syn::{
     parenthesized,
-    parse::{Parse, ParseBuffer},
+    parse::Parse,
     token::{Bracket, Paren},
-    LitBool, LitStr, Token,
+    Error, Token,
 };
 
-use crate::{property::Property, MediaType, Required};
+use crate::{parse_utils, MediaType, Required};
+
+use super::{property::Property, ContentTypeResolver};
 
 /// Parsed information related to requst body of path.
 ///
@@ -52,7 +54,7 @@ use crate::{property::Property, MediaType, Required};
 #[derive(Default)]
 #[cfg_attr(feature = "debug", derive(Debug))]
 pub struct RequestBodyAttr {
-    content: MediaType,
+    content: Option<MediaType>,
     content_type: Option<String>,
     required: Option<bool>,
     description: Option<String>,
@@ -60,14 +62,8 @@ pub struct RequestBodyAttr {
 
 impl Parse for RequestBodyAttr {
     fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let parse_lit_str = |group: &ParseBuffer| -> String {
-            if group.peek(Token![=]) {
-                group.parse::<Token![=]>().unwrap();
-            }
-            group.parse::<LitStr>().unwrap().value()
-        };
-
         let lookahead = input.lookahead1();
+
         if lookahead.peek(Paren) {
             let group;
             parenthesized!(group in input);
@@ -83,22 +79,33 @@ impl Parse for RequestBodyAttr {
                             group.parse::<Token![=]>().unwrap();
                         }
 
-                        request_body_attr.content = group.parse::<MediaType>().unwrap();
+                        request_body_attr.content = Some(group.parse::<MediaType>().unwrap());
+                    }
+                    "content_type" => {
+                        request_body_attr.content_type = Some(parse_utils::parse_next_lit_str(
+                            &group,
+                            "unparseable content type, expected LitStr",
+                        ))
                     }
-                    "content_type" => request_body_attr.content_type = Some(parse_lit_str(&group)),
                     "required" => {
-                        // support assign form as: required = bool
-                        if group.peek(Token![=]) && group.peek2(LitBool) {
-                            group.parse::<Token![=]>().unwrap();
-
-                            request_body_attr.required = Some(group.parse::<LitBool>().unwrap().value());
-                        } else {
-                            // quick form as: required
-                            request_body_attr.required = Some(true);
-                        }
+                        request_body_attr.required = Some(parse_utils::parse_bool_or_true(&group));
+                    }
+                    "description" => {
+                        request_body_attr.description = Some(parse_utils::parse_next_lit_str(
+                            &group,
+                            "unparseable description, expected LitStr",
+                        ))
+                    }
+                    _ => {
+                        return Err(Error::new(
+                            ident.span(),
+                            format!(
+                                "unexpedted attribute: {}, 
+                        expected values: content, content_type, required, description",
+                                &name
+                            ),
+                        ))
                     }
-                    "description" => request_body_attr.description = Some(parse_lit_str(&group)),
-                    _ => return Err(group.error(format!("unexpedted attribute: {}, expected values: content, content_type, required, description", &name)))
                 }
 
                 if group.peek(Token![,]) {
@@ -112,7 +119,7 @@ impl Parse for RequestBodyAttr {
             Ok(request_body_attr)
         } else if lookahead.peek(Bracket) || lookahead.peek(syn::Ident) {
             Ok(RequestBodyAttr {
-                content: input.parse().unwrap(),
+                content: Some(input.parse().unwrap()),
                 content_type: None,
                 description: None,
                 required: None,
@@ -123,22 +130,21 @@ impl Parse for RequestBodyAttr {
     }
 }
 
+impl ContentTypeResolver for RequestBodyAttr {}
+
 impl ToTokens for RequestBodyAttr {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
-        let property = Property::new(self.content.is_array, self.content.ty.as_ref().unwrap());
+        if let Some(ref body_type) = self.content {
+            let property = Property::new(body_type.is_array, &body_type.ty);
 
-        let content_type = if let Some(ref content_type) = self.content_type {
-            content_type
-        } else if property.component_type.is_primitive() {
-            "text/plain"
-        } else {
-            "application/json"
-        };
+            let content_type =
+                self.resolve_content_type(self.content_type.as_ref(), &property.component_type);
 
-        tokens.extend(quote! {
-            utoipa::openapi::request_body::RequestBody::new()
-                .with_content(#content_type, #property)
-        });
+            tokens.extend(quote! {
+                utoipa::openapi::request_body::RequestBody::new()
+                    .with_content(#content_type, #property)
+            });
+        }
 
         if let Some(required) = self.required {
             let required: Required = required.into();
diff --git a/utoipa-gen/src/response.rs b/utoipa-gen/src/path/response.rs
similarity index 85%
rename from utoipa-gen/src/response.rs
rename to utoipa-gen/src/path/response.rs
index c347e4d..e818efe 100644
--- a/utoipa-gen/src/response.rs
+++ b/utoipa-gen/src/path/response.rs
@@ -2,14 +2,12 @@ use proc_macro2::{Group, Ident, TokenStream as TokenStream2};
 use proc_macro_error::ResultExt;
 use quote::{quote, ToTokens};
 use syn::{
-    bracketed,
-    parse::{Parse, ParseStream},
-    punctuated::Punctuated,
-    token::Comma,
-    LitInt, LitStr, Token,
+    bracketed, parse::Parse, punctuated::Punctuated, token::Comma, Error, LitInt, LitStr, Token,
 };
 
-use crate::{property::Property, MediaType};
+use crate::{parse_utils, MediaType};
+
+use super::{property::Property, ContentTypeResolver};
 
 /// Parsed representation of response attributes from `#[utoipa::path]` attribute.
 ///
@@ -85,7 +83,7 @@ impl Parse for Response {
 
             match name {
                 "status" => {
-                    response.status_code = parse_next(&input, || {
+                    response.status_code = parse_utils::parse_next(input, || {
                         input
                             .parse::<LitInt>()
                             .unwrap()
@@ -94,21 +92,22 @@ impl Parse for Response {
                     });
                 }
                 "description" => {
-                    response.description =
-                        parse_next(&input, || input.parse::<LitStr>().unwrap_or_abort().value());
+                    response.description = parse_utils::parse_next(input, || {
+                        input.parse::<LitStr>().unwrap_or_abort().value()
+                    });
                 }
                 "body" => {
-                    response.response_type = Some(parse_next(&input, || {
+                    response.response_type = Some(parse_utils::parse_next(input, || {
                         input.parse::<MediaType>().unwrap_or_abort()
                     }));
                 }
                 "content_type" => {
-                    response.content_type = Some(parse_next(&input, || {
+                    response.content_type = Some(parse_utils::parse_next(input, || {
                         input.parse::<LitStr>().unwrap_or_abort().value()
                     }));
                 }
                 "headers" => {
-                    let groups = parse_next(&input, || {
+                    let groups = parse_utils::parse_next(input, || {
                         let content;
                         bracketed!(content in input);
                         Punctuated::<Group, Comma>::parse_terminated(&content)
@@ -126,7 +125,8 @@ impl Parse for Response {
                     expected values: status, description, body, content_type, headers",
                         &name
                     );
-                    return Err(input.error(error_msg));
+
+                    return Err(Error::new(ident.span(), error_msg));
                 }
             }
 
@@ -142,16 +142,10 @@ impl Parse for Response {
     }
 }
 
-#[inline]
-fn parse_next<T: Sized>(input: &ParseStream, next: impl FnOnce() -> T) -> T {
-    input
-        .parse::<Token![=]>()
-        .expect_or_abort("expected euqals sign token (=)");
-    next()
-}
-
 pub struct Responses<'a>(pub &'a [Response]);
 
+impl ContentTypeResolver for Responses<'_> {}
+
 impl ToTokens for Responses<'_> {
     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
         tokens.extend(quote! { utoipa::openapi::Responses::new() });
@@ -165,16 +159,14 @@ impl ToTokens for Responses<'_> {
             };
 
             if let Some(ref response_body_type) = response.response_type {
-                let body_type = response_body_type.ty.as_ref().unwrap();
+                let body_type = &response_body_type.ty;
 
                 let component = Property::new(response_body_type.is_array, body_type);
-                let content_type = if let Some(ref content_type) = response.content_type {
-                    content_type
-                } else if component.component_type.is_primitive() {
-                    "text/plain"
-                } else {
-                    "application/json"
-                };
+
+                let content_type = self.resolve_content_type(
+                    response.content_type.as_ref(),
+                    &component.component_type,
+                );
 
                 response_tokens.extend(quote! {
                     .with_content(#content_type, #component)
@@ -201,7 +193,7 @@ impl ToTokens for Responses<'_> {
 fn new_header_tokens(header: &Header) -> TokenStream2 {
     let mut header_tokens = if let Some(ref header_type) = header.media_type {
         // header property with custom type
-        let header_type = Property::new(header_type.is_array, header_type.ty.as_ref().unwrap());
+        let header_type = Property::new(header_type.is_array, &header_type.ty);
 
         quote! {
             utoipa::openapi::Header::new(#header_type)
@@ -297,7 +289,11 @@ impl Parse for Header {
         if input.peek(Token![=]) {
             input.parse::<Token![=]>().unwrap_or_abort();
 
-            header.media_type = Some(input.parse::<MediaType>().unwrap_or_abort());
+            header.media_type = Some(
+                input
+                    .parse::<MediaType>()
+                    .expect_or_abort("unparseable Header type, expected: Ident"),
+            );
         }
 
         if input.peek(Token![,]) {
@@ -305,9 +301,7 @@ impl Parse for Header {
         }
 
         if input.peek(syn::Ident) {
-            let description = input
-                .parse::<Ident>()
-                .expect_or_abort("unexpected attribute for Header description, expected Ident");
+            let description = input.parse::<Ident>().unwrap();
 
             if description == "description" {
                 if input.peek(Token![=]) {
@@ -317,7 +311,7 @@ impl Parse for Header {
                 let description = input.parse::<LitStr>().unwrap_or_abort().value();
                 header.description = Some(description);
             } else {
-                return Err(syn::Error::new(
+                return Err(Error::new(
                     description.span(),
                     format!(
                         "unexpected attribute: {}, expected: description",


Test patch:
diff --git a/tests/component_derive_test.rs b/tests/component_derive_test.rs
index a90cf65..11014dc 100644
--- a/tests/component_derive_test.rs
+++ b/tests/component_derive_test.rs
@@ -28,7 +28,7 @@ macro_rules! api_doc {
 
     ( @doc $name:ident ) => {{
         #[derive(OpenApi)]
-        #[openapi(handler_files = [], components = [$name])]
+        #[openapi(components = [$name])]
         struct ApiDoc;
 
         let json = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -304,3 +304,20 @@ fn derive_struct_unnamed_field_vec_type_success() {
         "items.format" = r#""int32""#, "Wrapper items format"
     }
 }
+
+#[test]
+#[ignore = "not supported yet!!!"]
+fn derive_struct_nested_vec_success() {
+    let vecs = api_doc! {
+        struct VecTest {
+            vecs: Vec<Vec<String>>
+        }
+    };
+
+    println!("{:#?}", vecs);
+    // assert_value! {point=>
+    //     "type" = r#""array""#, "Wrapper type"
+    //     "items.type" = r#""integer""#, "Wrapper items type"
+    //     "items.format" = r#""int32""#, "Wrapper items format"
+    // }
+}
diff --git a/tests/path_derive.rs b/tests/path_derive.rs
index 0532bc0..2402a8f 100644
--- a/tests/path_derive.rs
+++ b/tests/path_derive.rs
@@ -6,7 +6,7 @@ macro_rules! test_api_fn_doc {
     ( $handler:path, operation: $operation:expr, path: $path:literal ) => {{
         use utoipa::OpenApi;
         #[derive(OpenApi, Default)]
-        #[openapi(handler_files = [], handlers = [$handler])]
+        #[openapi(handlers = [$handler])]
         struct ApiDoc;
 
         let doc = &serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -58,7 +58,7 @@ macro_rules! test_path_operation {
             paste!{
                 use utoipa::OpenApi;
                 #[derive(OpenApi, Default)]
-                #[openapi(handler_files = [], handlers = [
+                #[openapi(handlers = [
                     [<mod_ $name>]::test_operation
                 ])]
                 struct ApiDoc;
diff --git a/tests/path_derive_actix.rs b/tests/path_derive_actix.rs
index 3a38b83..cf829ae 100644
--- a/tests/path_derive_actix.rs
+++ b/tests/path_derive_actix.rs
@@ -30,7 +30,7 @@ mod mod_derive_path_actix {
 #[test]
 fn derive_path_one_value_actix_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [mod_derive_path_actix::get_foo_by_id])]
+    #[openapi(handlers = [mod_derive_path_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -73,7 +73,7 @@ mod mod_derive_path_unnamed_regex_actix {
 #[test]
 fn derive_path_with_unnamed_regex_actix_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [mod_derive_path_unnamed_regex_actix::get_foo_by_id])]
+    #[openapi(handlers = [mod_derive_path_unnamed_regex_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -116,7 +116,7 @@ mod mod_derive_path_named_regex_actix {
 #[test]
 fn derive_path_with_named_regex_actix_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [mod_derive_path_named_regex_actix::get_foo_by_id])]
+    #[openapi(handlers = [mod_derive_path_named_regex_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -156,7 +156,7 @@ macro_rules! test_derive_path_operations {
         #[test]
         fn $name() {
             #[derive(OpenApi, Default)]
-            #[openapi(handler_files = [], handlers = [$mod::test_operation])]
+            #[openapi(handlers = [$mod::test_operation])]
             struct ApiDoc;
 
             let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
diff --git a/tests/path_parameter_derive_actix.rs b/tests/path_parameter_derive_actix.rs
index 9dca2fb..85d7bcc 100644
--- a/tests/path_parameter_derive_actix.rs
+++ b/tests/path_parameter_derive_actix.rs
@@ -31,7 +31,7 @@ mod derive_params_multiple_actix {
 #[test]
 fn derive_path_parameter_multiple_with_matching_names_and_types_actix_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_multiple_actix::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_multiple_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -85,7 +85,7 @@ mod derive_parameters_multiple_no_matching_names_actix {
 #[test]
 fn derive_path_parameter_multiple_no_matching_names_acitx_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_parameters_multiple_no_matching_names_actix::get_foo_by_id])]
+    #[openapi(handlers = [derive_parameters_multiple_no_matching_names_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -131,7 +131,7 @@ mod derive_params_from_method_args_actix {
 #[test]
 fn derive_params_from_method_args_actix_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_from_method_args_actix::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_from_method_args_actix::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
diff --git a/tests/path_parameter_derive_test.rs b/tests/path_parameter_derive_test.rs
index 19e3e16..83ba6eb 100644
--- a/tests/path_parameter_derive_test.rs
+++ b/tests/path_parameter_derive_test.rs
@@ -29,7 +29,7 @@ mod derive_params_all_options {
 #[test]
 fn derive_path_parameters_with_all_options_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_all_options::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_all_options::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -70,7 +70,7 @@ mod derive_params_minimal {
 #[test]
 fn derive_path_parameters_minimal_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_minimal::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_minimal::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -112,7 +112,7 @@ mod derive_params_multiple {
 #[test]
 fn derive_path_parameter_multiple_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_multiple::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_multiple::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -165,7 +165,7 @@ mod mod_derive_parameters_all_types {
 #[test]
 fn derive_parameters_with_all_types() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [mod_derive_parameters_all_types::get_foo_by_id])]
+    #[openapi(handlers = [mod_derive_parameters_all_types::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
@@ -237,7 +237,7 @@ mod derive_params_without_args {
 #[test]
 fn derive_params_without_fn_args() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_params_without_args::get_foo_by_id])]
+    #[openapi(handlers = [derive_params_without_args::get_foo_by_id])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(ApiDoc::openapi()).unwrap();
diff --git a/tests/path_response_derive_test.rs b/tests/path_response_derive_test.rs
index e1e7046..e2fa864 100644
--- a/tests/path_response_derive_test.rs
+++ b/tests/path_response_derive_test.rs
@@ -14,7 +14,7 @@ macro_rules! api_doc {
     ( module: $module:expr ) => {{
         use utoipa::OpenApi;
         #[derive(OpenApi, Default)]
-        #[openapi(handler_files = [], handlers = [$module::get_foo])]
+        #[openapi(handlers = [$module::get_foo])]
         struct ApiDoc;
 
         let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
diff --git a/tests/request_body_derive_test.rs b/tests/request_body_derive_test.rs
index 660c962..96b0026 100644
--- a/tests/request_body_derive_test.rs
+++ b/tests/request_body_derive_test.rs
@@ -31,7 +31,7 @@ test_fn! {
 #[test]
 fn derive_path_request_body_simple_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_simple::post_foo])]
+    #[openapi(handlers = [derive_request_body_simple::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -52,7 +52,7 @@ test_fn! {
 #[test]
 fn derive_path_request_body_simple_array_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_simple_array::post_foo])]
+    #[openapi(handlers = [derive_request_body_simple_array::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -75,7 +75,7 @@ test_fn! {
 #[test]
 fn derive_request_body_primitive_simple_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_primitive_simple::post_foo])]
+    #[openapi(handlers = [derive_request_body_primitive_simple::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -98,7 +98,7 @@ test_fn! {
 #[test]
 fn derive_request_body_primitive_array_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_primitive_simple_array::post_foo])]
+    #[openapi(handlers = [derive_request_body_primitive_simple_array::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -121,7 +121,7 @@ test_fn! {
 #[test]
 fn derive_request_body_complex_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_complex::post_foo])]
+    #[openapi(handlers = [derive_request_body_complex::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -144,7 +144,7 @@ test_fn! {
 #[test]
 fn derive_request_body_complex_required_explisit_false_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_complex_required_explisit::post_foo])]
+    #[openapi(handlers = [derive_request_body_complex_required_explisit::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
@@ -167,7 +167,7 @@ test_fn! {
 #[test]
 fn derive_request_body_complex_primitive_array_success() {
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [derive_request_body_complex_primitive_array::post_foo])]
+    #[openapi(handlers = [derive_request_body_complex_primitive_array::post_foo])]
     struct ApiDoc;
 
     let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap();
diff --git a/tests/utoipa_gen_test.rs b/tests/utoipa_gen_test.rs
index dc9ea38..56d77fb 100644
--- a/tests/utoipa_gen_test.rs
+++ b/tests/utoipa_gen_test.rs
@@ -1,11 +1,11 @@
 #![cfg(feature = "actix_extras")]
 use actix_web::{get, web, HttpResponse, Responder};
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
 use serde_json::json;
 // use utoipa::openapi_spec;
-use utoipa::OpenApi;
+use utoipa::{Component, OpenApi};
 
-#[derive(Deserialize)]
+#[derive(Deserialize, Serialize, Component)]
 struct Foo {
     ids: Vec<i32>,
 }
@@ -42,7 +42,7 @@ async fn foo_delete(web::Query(foo): web::Query<Foo>) -> impl Responder {
 fn derive_openapi() {
     // use crate::api::__path_foo_delete;
     #[derive(OpenApi, Default)]
-    #[openapi(handler_files = [], handlers = [foo_delete])]
+    #[openapi(handlers = [foo_delete], components = [Foo])]
     struct ApiDoc;
 
     println!("{}", ApiDoc::openapi().to_pretty_json().unwrap());